From d54b5861a30b1b1a352e1c22fc67e729ec95390d Mon Sep 17 00:00:00 2001 From: Dan Halbert Date: Fri, 12 Feb 2021 19:01:14 -0500 Subject: wip --- 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 +++++++++++++++++++++++ 4 files changed, 169 insertions(+) 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-module') 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 */ -- 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-module') 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-module') 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-module') 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-module') 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-module') 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 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-module') 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 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-module') 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 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-module') 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 93bf269c0d56ec5d842ff908e149c635ab323107 Mon Sep 17 00:00:00 2001 From: Dan Halbert Date: Sun, 21 Feb 2021 12:00:32 -0500 Subject: Avoid pulling in extra float-uint64 routines --- py/obj.h | 1 + py/objfloat.c | 9 +++++++++ shared-module/usb_cdc/Serial.c | 6 ++++-- 3 files changed, 14 insertions(+), 2 deletions(-) (limited to 'shared-module') diff --git a/py/obj.h b/py/obj.h index 066562cc4..c3ab63b5c 100644 --- a/py/obj.h +++ b/py/obj.h @@ -684,6 +684,7 @@ mp_obj_t mp_obj_new_bytearray_by_ref(size_t n, void *items); mp_obj_t mp_obj_new_int_from_float(mp_float_t val); mp_obj_t mp_obj_new_complex(mp_float_t real, mp_float_t imag); extern mp_float_t uint64_to_float(uint64_t ui64); +extern uint64_t float_to_uint64(float f); #endif mp_obj_t mp_obj_new_exception(const mp_obj_type_t *exc_type); mp_obj_t mp_obj_new_exception_arg1(const mp_obj_type_t *exc_type, mp_obj_t arg); diff --git a/py/objfloat.c b/py/objfloat.c index 80f10e816..329a8b90e 100644 --- a/py/objfloat.c +++ b/py/objfloat.c @@ -340,6 +340,15 @@ mp_float_t uint64_to_float(uint64_t ui64) { return (mp_float_t) ((uint32_t) (ui64 >> 32) * 4294967296.0f + (uint32_t) (ui64 & 0xffffffff)); } +// Convert a uint64_t to a 32-bit float to a uint64_t without invoking extra math routines. +// which are large. +// Assume f >= 0. +uint64_t float_to_uint64(float f) { + // 4294967296 = 2^32 + const uint32_t upper_half = (uint32_t) (f / 4294967296.0f); + const uint32_t lower_half = (uint32_t) f; + return (((uint64_t) upper_half) << 32) + lower_half; +} #pragma GCC diagnostic pop #endif // MICROPY_PY_BUILTINS_FLOAT diff --git a/shared-module/usb_cdc/Serial.c b/shared-module/usb_cdc/Serial.c index 6037fddcc..b4d8667ca 100644 --- a/shared-module/usb_cdc/Serial.c +++ b/shared-module/usb_cdc/Serial.c @@ -41,7 +41,8 @@ size_t common_hal_usb_cdc_serial_read(usb_cdc_serial_obj_t *self, uint8_t *data, if (wait_forever || wait_for_timeout) { // Read more if we have time. - uint64_t timeout_ms = self->timeout * 1000; // Junk value if timeout < 0. + // Use special routine to avoid pulling in uint64-float-compatible math routines. + uint64_t timeout_ms = float_to_uint64(self->timeout * 1000); // Junk value if timeout < 0. uint64_t start_ticks = supervisor_ticks_ms64(); uint32_t num_read = 0; @@ -78,7 +79,8 @@ size_t common_hal_usb_cdc_serial_write(usb_cdc_serial_obj_t *self, const uint8_t 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. + // Use special routine to avoid pulling in uint64-float-compatible math routines. + uint64_t timeout_ms = float_to_uint64(self->write_timeout * 1000); // Junk value if write_timeout < 0. uint64_t start_ticks = supervisor_ticks_ms64(); uint32_t num_written = 0; -- 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-module') 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 b7f5c277ad61076848dfc2065bc254092f4dd4ce Mon Sep 17 00:00:00 2001 From: Jeff Epler Date: Wed, 24 Feb 2021 09:29:59 -0600 Subject: _pixelbuf: Increase performance of brightness-scaling On the Pico, this increases the "fill rate" of pixels[:] = newvalues considerably. On a strip of 240 RGB LEDs, auto_write=False, the timings are: || Brightness || Before || After || Improvement || || 1.0 || 117 kpix/s || 307 kpix/s || 2.62x || || 0.07 || 117 kpix/s || 273 kpix/s || 2.33x || It's worth noting that even the "before" rate is fast compared to the time to transmit a single neopixel, but any time we can gain back in the whole pipeline will let marginal animations work a little better. To set all the pixels in this way and then show() gives a pleasant bump to the framerate, from about 108Hz to 124Hz (1.15x) The main source of speed-up is using integer math instead of floating point math for the calculation of the post-scaled pixel values. A slight secondary gain is achieved by avoiding the scaling altogether when the scale factor is 1.0. Because the math is not exactly the same, some scaled pixel values may change by +- 1 RGBW "step". In practice, this is unlikely to matter. The gains are bigger on the Pico and other M0 microcontrollers than M4 microcontrollers with floating point math in the hardware. Happily, flash size is also improved a bit on the Pico build I did, going from > 542552 bytes used, 506024 bytes free in flash firmware space out of 1048576 bytes (1024.0kB). to > 542376 bytes used, 506200 bytes free in flash firmware space out of 1048576 bytes (1024.0kB). --- shared-module/_pixelbuf/PixelBuf.c | 97 +++++++++++++++++++++++++------------- shared-module/_pixelbuf/PixelBuf.h | 3 +- 2 files changed, 67 insertions(+), 33 deletions(-) (limited to 'shared-module') diff --git a/shared-module/_pixelbuf/PixelBuf.c b/shared-module/_pixelbuf/PixelBuf.c index f3e679631..f11b03cbb 100644 --- a/shared-module/_pixelbuf/PixelBuf.c +++ b/shared-module/_pixelbuf/PixelBuf.c @@ -31,6 +31,7 @@ #include "py/runtime.h" #include "shared-bindings/_pixelbuf/PixelBuf.h" #include +#include // Helper to ensure we have the native super class instead of a subclass. static pixelbuf_pixelbuf_obj_t* native_pixelbuf(mp_obj_t pixelbuf_obj) { @@ -69,6 +70,7 @@ void common_hal__pixelbuf_pixelbuf_construct(pixelbuf_pixelbuf_obj_t *self, size } // Call set_brightness so that it can allocate a second buffer if needed. self->brightness = 1.0; + self->scaled_brightness = 0x100; common_hal__pixelbuf_pixelbuf_set_brightness(MP_OBJ_FROM_PTR(self), brightness); // Turn on auto_write. We don't want to do it with the above brightness call. @@ -109,26 +111,31 @@ void common_hal__pixelbuf_pixelbuf_set_brightness(mp_obj_t self_in, mp_float_t b pixelbuf_pixelbuf_obj_t* self = native_pixelbuf(self_in); // Skip out if the brightness is already set. The default of self->brightness is 1.0. So, this // also prevents the pre_brightness_buffer allocation when brightness is set to 1.0 again. - mp_float_t change = brightness - self->brightness; - if (-0.001 < change && change < 0.001) { + self->brightness = brightness; + uint16_t new_scaled_brightness = (int)roundf(brightness * 256); + if (new_scaled_brightness == self->scaled_brightness) { return; } - self->brightness = brightness; + self->scaled_brightness = new_scaled_brightness; size_t pixel_len = self->pixel_count * self->bytes_per_pixel; - if (self->pre_brightness_buffer == NULL) { - self->pre_brightness_buffer = m_malloc(pixel_len, false); - memcpy(self->pre_brightness_buffer, self->post_brightness_buffer, pixel_len); - } - for (size_t i = 0; i < pixel_len; i++) { - // Don't adjust per-pixel luminance bytes in dotstar mode - if (self->byteorder.is_dotstar && i % 4 == 0) { - continue; + if (self->scaled_brightness == 0x100 && !self->pre_brightness_buffer) { + return; + } else { + if (self->pre_brightness_buffer == NULL) { + self->pre_brightness_buffer = m_malloc(pixel_len, false); + memcpy(self->pre_brightness_buffer, self->post_brightness_buffer, pixel_len); + } + for (size_t i = 0; i < pixel_len; i++) { + // Don't adjust per-pixel luminance bytes in dotstar mode + if (self->byteorder.is_dotstar && i % 4 == 0) { + continue; + } + self->post_brightness_buffer[i] = (self->pre_brightness_buffer[i] * self->scaled_brightness) >> 8; } - self->post_brightness_buffer[i] = self->pre_brightness_buffer[i] * self->brightness; - } - if (self->auto_write) { - common_hal__pixelbuf_pixelbuf_show(self_in); + if (self->auto_write) { + common_hal__pixelbuf_pixelbuf_show(self_in); + } } } @@ -197,28 +204,34 @@ void _pixelbuf_set_pixel_color(pixelbuf_pixelbuf_obj_t* self, size_t index, uint } pixelbuf_rgbw_t *rgbw_order = &self->byteorder.byteorder; size_t offset = index * self->bytes_per_pixel; - if (self->pre_brightness_buffer != NULL) { - uint8_t* pre_brightness_buffer = self->pre_brightness_buffer + offset; - if (self->bytes_per_pixel == 4) { - pre_brightness_buffer[rgbw_order->w] = w; - } - - pre_brightness_buffer[rgbw_order->r] = r; - pre_brightness_buffer[rgbw_order->g] = g; - pre_brightness_buffer[rgbw_order->b] = b; + uint8_t *scaled_buffer, *unscaled_buffer; + if (self->pre_brightness_buffer) { + scaled_buffer = self->post_brightness_buffer + offset; + unscaled_buffer = self->pre_brightness_buffer + offset; + } else { + scaled_buffer = NULL; + unscaled_buffer = self->post_brightness_buffer + offset; } - uint8_t* post_brightness_buffer = self->post_brightness_buffer + offset; if (self->bytes_per_pixel == 4) { - // Only apply brightness if w is actually white (aka not DotStar.) - if (!self->byteorder.is_dotstar) { - w *= self->brightness; + unscaled_buffer[rgbw_order->w] = w; + } + + unscaled_buffer[rgbw_order->r] = r; + unscaled_buffer[rgbw_order->g] = g; + unscaled_buffer[rgbw_order->b] = b; + + if (scaled_buffer) { + if (self->bytes_per_pixel == 4) { + if (!self->byteorder.is_dotstar) { + w = (w * self->scaled_brightness) >> 8; + } + scaled_buffer[rgbw_order->w] = w; } - post_brightness_buffer[rgbw_order->w] = w; + scaled_buffer[rgbw_order->r] = (r * self->scaled_brightness) >> 8; + scaled_buffer[rgbw_order->g] = (g * self->scaled_brightness) >> 8; + scaled_buffer[rgbw_order->b] = (b * self->scaled_brightness) >> 8; } - post_brightness_buffer[rgbw_order->r] = r * self->brightness; - post_brightness_buffer[rgbw_order->g] = g * self->brightness; - post_brightness_buffer[rgbw_order->b] = b * self->brightness; } void _pixelbuf_set_pixel(pixelbuf_pixelbuf_obj_t* self, size_t index, mp_obj_t value) { @@ -318,3 +331,23 @@ void common_hal__pixelbuf_pixelbuf_fill(mp_obj_t self_in, mp_obj_t fill_color) { common_hal__pixelbuf_pixelbuf_show(self_in); } } + +mp_int_t common_hal__pixelbuf_get_buffer(mp_obj_t self_in, mp_buffer_info_t *bufinfo, mp_uint_t flags) { + pixelbuf_pixelbuf_obj_t *self = native_pixelbuf(self_in); + bufinfo->buf = self->pre_brightness_buffer; + if (self->pre_brightness_buffer) { + // If we have a brightness setting, we must treat the buffer as + // read-only (because we have no way to "fire" the + // brightness-converting code as a side effect of mutation via the + // buffer) + if ((flags & MP_BUFFER_WRITE)) { + return 1; + } + bufinfo->buf = self->pre_brightness_buffer; + } else { + bufinfo->buf = self->post_brightness_buffer; + } + bufinfo->typecode = 'B'; + bufinfo->len = self->bytes_per_pixel * common_hal__pixelbuf_pixelbuf_get_len(self_in); + return 0; +} diff --git a/shared-module/_pixelbuf/PixelBuf.h b/shared-module/_pixelbuf/PixelBuf.h index a9fbed366..f0c80bcad 100644 --- a/shared-module/_pixelbuf/PixelBuf.h +++ b/shared-module/_pixelbuf/PixelBuf.h @@ -49,7 +49,8 @@ typedef struct { typedef struct { mp_obj_base_t base; size_t pixel_count; - size_t bytes_per_pixel; + uint16_t bytes_per_pixel; + uint16_t scaled_brightness; pixelbuf_byteorder_details_t byteorder; mp_float_t brightness; mp_obj_t transmit_buffer_obj; -- cgit v1.2.3 From ef3a61432be5bf7a4778607d49b3ed49cb141cf7 Mon Sep 17 00:00:00 2001 From: Kamil Tomaszewski Date: Wed, 24 Feb 2021 19:09:17 +0100 Subject: Add the missing argument to the HID functions --- shared-module/usb_hid/Device.c | 6 ++++-- supervisor/shared/usb/usb_desc.c | 5 +++-- 2 files changed, 7 insertions(+), 4 deletions(-) (limited to 'shared-module') diff --git a/shared-module/usb_hid/Device.c b/shared-module/usb_hid/Device.c index 943c9bfed..2b560c328 100644 --- a/shared-module/usb_hid/Device.c +++ b/shared-module/usb_hid/Device.c @@ -73,7 +73,8 @@ static usb_hid_device_obj_t* get_hid_device(uint8_t report_id) { } // Callbacks invoked when receive Get_Report request through control endpoint -uint16_t tud_hid_get_report_cb(uint8_t report_id, hid_report_type_t report_type, uint8_t* buffer, uint16_t reqlen) { +uint16_t tud_hid_get_report_cb(uint8_t itf, uint8_t report_id, hid_report_type_t report_type, uint8_t* buffer, uint16_t reqlen) { + (void) itf; // only support Input Report if ( report_type != HID_REPORT_TYPE_INPUT ) return 0; @@ -83,7 +84,8 @@ uint16_t tud_hid_get_report_cb(uint8_t report_id, hid_report_type_t report_type, } // Callbacks invoked when receive Set_Report request through control endpoint -void tud_hid_set_report_cb(uint8_t report_id, hid_report_type_t report_type, uint8_t const* buffer, uint16_t bufsize) { +void tud_hid_set_report_cb(uint8_t itf, uint8_t report_id, hid_report_type_t report_type, uint8_t const* buffer, uint16_t bufsize) { + (void) itf; if (report_type == HID_REPORT_TYPE_INVALID) { report_id = buffer[0]; buffer++; diff --git a/supervisor/shared/usb/usb_desc.c b/supervisor/shared/usb/usb_desc.c index 419a70ab3..b08d3b227 100644 --- a/supervisor/shared/usb/usb_desc.c +++ b/supervisor/shared/usb/usb_desc.c @@ -47,8 +47,9 @@ uint8_t const * tud_descriptor_configuration_cb(uint8_t index) { // Invoked when received GET HID REPORT DESCRIPTOR // Application return pointer to descriptor // Descriptor contents must exist long enough for transfer to complete -uint8_t const * tud_hid_descriptor_report_cb(void) { - return hid_report_descriptor; +uint8_t const * tud_hid_descriptor_report_cb(uint8_t itf) { + (void) itf; + return hid_report_descriptor; } #endif -- cgit v1.2.3 From dac4ac5d2a294efea12ad8cea6254a94fe4ec765 Mon Sep 17 00:00:00 2001 From: Jeff Epler Date: Wed, 24 Feb 2021 16:27:09 -0600 Subject: _pixelbuf: Respond to review comments * Comment on the reason for scaling by 256 * Divide by 256 instead of shifting * fix a cast; eliminate an unneeded roundf() to get a few bytes code back --- shared-module/_pixelbuf/PixelBuf.c | 33 +++++++-------------------------- 1 file changed, 7 insertions(+), 26 deletions(-) (limited to 'shared-module') diff --git a/shared-module/_pixelbuf/PixelBuf.c b/shared-module/_pixelbuf/PixelBuf.c index f11b03cbb..4cbf6dc21 100644 --- a/shared-module/_pixelbuf/PixelBuf.c +++ b/shared-module/_pixelbuf/PixelBuf.c @@ -112,7 +112,8 @@ void common_hal__pixelbuf_pixelbuf_set_brightness(mp_obj_t self_in, mp_float_t b // Skip out if the brightness is already set. The default of self->brightness is 1.0. So, this // also prevents the pre_brightness_buffer allocation when brightness is set to 1.0 again. self->brightness = brightness; - uint16_t new_scaled_brightness = (int)roundf(brightness * 256); + // Use 256 steps of brightness so that we can do integer math below. + uint16_t new_scaled_brightness = (uint16_t)(brightness * 256); if (new_scaled_brightness == self->scaled_brightness) { return; } @@ -130,7 +131,7 @@ void common_hal__pixelbuf_pixelbuf_set_brightness(mp_obj_t self_in, mp_float_t b if (self->byteorder.is_dotstar && i % 4 == 0) { continue; } - self->post_brightness_buffer[i] = (self->pre_brightness_buffer[i] * self->scaled_brightness) >> 8; + self->post_brightness_buffer[i] = (self->pre_brightness_buffer[i] * self->scaled_brightness) / 256; } if (self->auto_write) { @@ -224,13 +225,13 @@ void _pixelbuf_set_pixel_color(pixelbuf_pixelbuf_obj_t* self, size_t index, uint if (scaled_buffer) { if (self->bytes_per_pixel == 4) { if (!self->byteorder.is_dotstar) { - w = (w * self->scaled_brightness) >> 8; + w = (w * self->scaled_brightness) / 256; } scaled_buffer[rgbw_order->w] = w; } - scaled_buffer[rgbw_order->r] = (r * self->scaled_brightness) >> 8; - scaled_buffer[rgbw_order->g] = (g * self->scaled_brightness) >> 8; - scaled_buffer[rgbw_order->b] = (b * self->scaled_brightness) >> 8; + scaled_buffer[rgbw_order->r] = (r * self->scaled_brightness) / 256; + scaled_buffer[rgbw_order->g] = (g * self->scaled_brightness) / 256; + scaled_buffer[rgbw_order->b] = (b * self->scaled_brightness) / 256; } } @@ -331,23 +332,3 @@ void common_hal__pixelbuf_pixelbuf_fill(mp_obj_t self_in, mp_obj_t fill_color) { common_hal__pixelbuf_pixelbuf_show(self_in); } } - -mp_int_t common_hal__pixelbuf_get_buffer(mp_obj_t self_in, mp_buffer_info_t *bufinfo, mp_uint_t flags) { - pixelbuf_pixelbuf_obj_t *self = native_pixelbuf(self_in); - bufinfo->buf = self->pre_brightness_buffer; - if (self->pre_brightness_buffer) { - // If we have a brightness setting, we must treat the buffer as - // read-only (because we have no way to "fire" the - // brightness-converting code as a side effect of mutation via the - // buffer) - if ((flags & MP_BUFFER_WRITE)) { - return 1; - } - bufinfo->buf = self->pre_brightness_buffer; - } else { - bufinfo->buf = self->post_brightness_buffer; - } - bufinfo->typecode = 'B'; - bufinfo->len = self->bytes_per_pixel * common_hal__pixelbuf_pixelbuf_get_len(self_in); - return 0; -} -- cgit v1.2.3 From 4097c949a33e53e378cecb1b50fb455f6f209bfb Mon Sep 17 00:00:00 2001 From: Kevin Matocha Date: Thu, 25 Feb 2021 14:16:40 -0600 Subject: Update for clean blitting into itself --- shared-module/displayio/Bitmap.c | 29 ++++++++++++++++++++++++----- 1 file changed, 24 insertions(+), 5 deletions(-) (limited to 'shared-module') diff --git a/shared-module/displayio/Bitmap.c b/shared-module/displayio/Bitmap.c index d1942efc7..cf34f2d35 100644 --- a/shared-module/displayio/Bitmap.c +++ b/shared-module/displayio/Bitmap.c @@ -116,14 +116,33 @@ void common_hal_displayio_bitmap_blit(displayio_bitmap_t *self, int16_t x, int16 mp_raise_RuntimeError(translate("Read-only object")); } + bool x_reverse = false; + bool y_reverse = false; + + // Add reverse direction option to protect blitting of self bitmap back into self bitmap + if (x > x1) { + x_reverse = true; + } + if (y > y1) { + y_reverse = true; + } + // simplest version - use internal functions for get/set pixels - for (int16_t i=0; i < (x2-x1) ; i++) { - if ( (x+i >= 0) && (x+i < self->width) ) { + for (int16_t i=0; i < (x2-x1); i++) { + + const int xs_index = x_reverse ? ( (x2 - 1) - i ) : x1+i; // x-index into the source bitmap + const int xd_index = x_reverse ? ((x + (x2-x1)) - i ) : x+i; // x-index into the destination bitmap + + if ( (xd_index >= 0) && (xd_index < self->width) ) { for (int16_t j=0; j < (y2-y1) ; j++){ - if ((y+j >= 0) && (y+j < self->height) ) { - uint32_t value = common_hal_displayio_bitmap_get_pixel(source, x1+i, y1+j); + + const int ys_index = y_reverse ? ( (y2 - 1) - j ) : y1+j ; // y-index into the source bitmap + const int yd_index = y_reverse ? ((y + (y2-y1)) - j ) : y+j ; // y-index into the destination bitmap + + if ((yd_index >= 0) && (yd_index < self->height) ) { + uint32_t value = common_hal_displayio_bitmap_get_pixel(source, xs_index, ys_index); if ( (skip_index_none) || (value != skip_index) ) { // write if skip_value_none is True - common_hal_displayio_bitmap_set_pixel(self, x+i, y+j, value); + common_hal_displayio_bitmap_set_pixel(self, xd_index, yd_index, value); } } } -- cgit v1.2.3 From 3afc269b18dd8239b170ecbbe47e506306b3db8a Mon Sep 17 00:00:00 2001 From: Kevin Matocha Date: Fri, 26 Feb 2021 17:22:11 -0600 Subject: fix two off by one errors --- shared-module/displayio/Bitmap.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'shared-module') diff --git a/shared-module/displayio/Bitmap.c b/shared-module/displayio/Bitmap.c index cf34f2d35..c9ea83428 100644 --- a/shared-module/displayio/Bitmap.c +++ b/shared-module/displayio/Bitmap.c @@ -130,14 +130,14 @@ void common_hal_displayio_bitmap_blit(displayio_bitmap_t *self, int16_t x, int16 // simplest version - use internal functions for get/set pixels for (int16_t i=0; i < (x2-x1); i++) { - const int xs_index = x_reverse ? ( (x2 - 1) - i ) : x1+i; // x-index into the source bitmap - const int xd_index = x_reverse ? ((x + (x2-x1)) - i ) : x+i; // x-index into the destination bitmap + const int xs_index = x_reverse ? ( (x2) - i - 1) : x1+i; // x-index into the source bitmap + const int xd_index = x_reverse ? ((x + (x2-x1)) - i - 1) : x+i; // x-index into the destination bitmap if ( (xd_index >= 0) && (xd_index < self->width) ) { for (int16_t j=0; j < (y2-y1) ; j++){ - const int ys_index = y_reverse ? ( (y2 - 1) - j ) : y1+j ; // y-index into the source bitmap - const int yd_index = y_reverse ? ((y + (y2-y1)) - j ) : y+j ; // y-index into the destination bitmap + const int ys_index = y_reverse ? ( (y2 ) - j - 1) : y1+j ; // y-index into the source bitmap + const int yd_index = y_reverse ? ((y + (y2-y1)) - j - 1) : y+j ; // y-index into the destination bitmap if ((yd_index >= 0) && (yd_index < self->height) ) { uint32_t value = common_hal_displayio_bitmap_get_pixel(source, xs_index, ys_index); -- cgit v1.2.3 From 6e0ce23f3e2ce341bcaefdd0a0d5c98ff43949f0 Mon Sep 17 00:00:00 2001 From: Radomir Dopieralski Date: Sat, 20 Feb 2021 23:04:06 +0100 Subject: displaio: don't store group children native object Since we want to expose the list of group's children to the user, we should only have the original objects in it, without any other additional data, and compute the native object as needed. --- shared-module/displayio/Group.c | 173 ++++++++++++++++++++++++++-------------- shared-module/displayio/Group.h | 1 - supervisor/shared/display.c | 6 +- 3 files changed, 117 insertions(+), 63 deletions(-) (limited to 'shared-module') diff --git a/shared-module/displayio/Group.c b/shared-module/displayio/Group.c index 15cf5b8e4..ba3bd4869 100644 --- a/shared-module/displayio/Group.c +++ b/shared-module/displayio/Group.c @@ -52,11 +52,18 @@ void common_hal_displayio_group_set_hidden(displayio_group_t* self, bool hidden) return; } for (size_t i = 0; i < self->size; i++) { - mp_obj_t layer = self->children[i].native; - if (MP_OBJ_IS_TYPE(layer, &displayio_tilegrid_type)) { + mp_obj_t layer; + layer = mp_instance_cast_to_native_base( + self->children[i].original, &displayio_tilegrid_type); + if (layer != MP_OBJ_NULL) { displayio_tilegrid_set_hidden_by_parent(layer, hidden); - } else if (MP_OBJ_IS_TYPE(layer, &displayio_group_type)) { + continue; + } + layer = mp_instance_cast_to_native_base( + self->children[i].original, &displayio_group_type); + if (layer != MP_OBJ_NULL) { displayio_group_set_hidden_by_parent(layer, hidden); + continue; } } } @@ -71,11 +78,18 @@ void displayio_group_set_hidden_by_parent(displayio_group_t *self, bool hidden) return; } for (size_t i = 0; i < self->size; i++) { - mp_obj_t layer = self->children[i].native; - if (MP_OBJ_IS_TYPE(layer, &displayio_tilegrid_type)) { + mp_obj_t layer; + layer = mp_instance_cast_to_native_base( + self->children[i].original, &displayio_tilegrid_type); + if (layer != MP_OBJ_NULL) { displayio_tilegrid_set_hidden_by_parent(layer, hidden); - } else if (MP_OBJ_IS_TYPE(layer, &displayio_group_type)) { + continue; + } + layer = mp_instance_cast_to_native_base( + self->children[i].original, &displayio_group_type); + if (layer != MP_OBJ_NULL) { displayio_group_set_hidden_by_parent(layer, hidden); + continue; } } } @@ -87,15 +101,21 @@ uint32_t common_hal_displayio_group_get_scale(displayio_group_t* self) { bool displayio_group_get_previous_area(displayio_group_t *self, displayio_area_t* area) { bool first = true; for (size_t i = 0; i < self->size; i++) { - mp_obj_t layer = self->children[i].native; + mp_obj_t layer; displayio_area_t layer_area; - if (MP_OBJ_IS_TYPE(layer, &displayio_tilegrid_type)) { + layer = mp_instance_cast_to_native_base( + self->children[i].original, &displayio_tilegrid_type); + if (layer != MP_OBJ_NULL) { if (!displayio_tilegrid_get_previous_area(layer, &layer_area)) { continue; } - } else if (MP_OBJ_IS_TYPE(layer, &displayio_group_type)) { - if (!displayio_group_get_previous_area(layer, &layer_area)) { - continue; + } else { + layer = mp_instance_cast_to_native_base( + self->children[i].original, &displayio_group_type); + if (layer != MP_OBJ_NULL) { + if (!displayio_group_get_previous_area(layer, &layer_area)) { + continue; + } } } if (first) { @@ -121,17 +141,26 @@ static void _update_child_transforms(displayio_group_t* self) { return; } for (size_t i = 0; i < self->size; i++) { - mp_obj_t layer = self->children[i].native; + mp_obj_t layer; #if CIRCUITPY_VECTORIO - if (MP_OBJ_IS_TYPE(layer, &vectorio_vector_shape_type)) { + layer = mp_instance_cast_to_native_base( + self->children[i].original, &vectorio_vector_shape_type); + if (layer != MP_OBJ_NULL) { vectorio_vector_shape_update_transform(layer, &self->absolute_transform); + continue; } - else #endif - if (MP_OBJ_IS_TYPE(layer, &displayio_tilegrid_type)) { + layer = mp_instance_cast_to_native_base( + self->children[i].original, &displayio_tilegrid_type); + if (layer != MP_OBJ_NULL) { displayio_tilegrid_update_transform(layer, &self->absolute_transform); - } else if (MP_OBJ_IS_TYPE(layer, &displayio_group_type)) { + continue; + } + layer = mp_instance_cast_to_native_base( + self->children[i].original, &displayio_group_type); + if (layer != MP_OBJ_NULL) { displayio_group_update_transform(layer, &self->absolute_transform); + continue; } } } @@ -210,21 +239,17 @@ void common_hal_displayio_group_set_y(displayio_group_t* self, mp_int_t y) { _update_child_transforms(self); } -static mp_obj_t _add_layer(displayio_group_t* self, mp_obj_t layer) { +static void _add_layer(displayio_group_t* self, mp_obj_t layer) { mp_obj_t native_layer; #if CIRCUITPY_VECTORIO native_layer = mp_instance_cast_to_native_base(layer, &vectorio_vector_shape_type); if (native_layer != MP_OBJ_NULL) { vectorio_vector_shape_update_transform(native_layer, &self->absolute_transform); - return native_layer; + return; } #endif - native_layer = mp_instance_cast_to_native_base(layer, &displayio_group_type); - if (native_layer == MP_OBJ_NULL) { - native_layer = mp_instance_cast_to_native_base(layer, &displayio_tilegrid_type); - if (native_layer == MP_OBJ_NULL) { - mp_raise_ValueError(translate("Layer must be a Group or TileGrid subclass.")); - } + native_layer = mp_instance_cast_to_native_base(layer, &displayio_tilegrid_type); + if (native_layer != MP_OBJ_NULL) { displayio_tilegrid_t* tilegrid = native_layer; if (tilegrid->in_group) { mp_raise_ValueError(translate("Layer already in a group.")); @@ -232,7 +257,10 @@ static mp_obj_t _add_layer(displayio_group_t* self, mp_obj_t layer) { tilegrid->in_group = true; } displayio_tilegrid_update_transform(tilegrid, &self->absolute_transform); - } else { + return; + } + native_layer = mp_instance_cast_to_native_base(layer, &displayio_group_type); + if (native_layer != MP_OBJ_NULL) { displayio_group_t* group = native_layer; if (group->in_group) { mp_raise_ValueError(translate("Layer already in a group.")); @@ -240,27 +268,34 @@ static mp_obj_t _add_layer(displayio_group_t* self, mp_obj_t layer) { group->in_group = true; } displayio_group_update_transform(group, &self->absolute_transform); + return; } - return native_layer; + mp_raise_ValueError(translate("Layer must be a Group or TileGrid subclass.")); } static void _remove_layer(displayio_group_t* self, size_t index) { - mp_obj_t layer = self->children[index].native; + mp_obj_t layer; displayio_area_t layer_area; bool rendered_last_frame = false; #if CIRCUITPY_VECTORIO - if (MP_OBJ_IS_TYPE(layer, &vectorio_vector_shape_type)) { + layer = mp_instance_cast_to_native_base( + self->children[index].original, &vectorio_vector_shape_type); + if (layer != MP_OBJ_NULL) { bool has_dirty_area = vectorio_vector_shape_get_dirty_area(layer, &layer_area); rendered_last_frame = has_dirty_area; vectorio_vector_shape_update_transform(layer, NULL); } - else #endif - if (MP_OBJ_IS_TYPE(layer, &displayio_tilegrid_type)) { + layer = mp_instance_cast_to_native_base( + self->children[index].original, &displayio_tilegrid_type); + if (layer != MP_OBJ_NULL) { displayio_tilegrid_t* tilegrid = layer; rendered_last_frame = displayio_tilegrid_get_previous_area(tilegrid, &layer_area); displayio_tilegrid_update_transform(tilegrid, NULL); - } else if (MP_OBJ_IS_TYPE(layer, &displayio_group_type)) { + } + layer = mp_instance_cast_to_native_base( + self->children[index].original, &displayio_group_type); + if (layer != MP_OBJ_NULL) { displayio_group_t* group = layer; rendered_last_frame = displayio_group_get_previous_area(group, &layer_area); displayio_group_update_transform(group, NULL); @@ -280,12 +315,11 @@ void common_hal_displayio_group_insert(displayio_group_t* self, size_t index, mp if (self->size == self->max_size) { mp_raise_RuntimeError(translate("Group full")); } - mp_obj_t native_layer = _add_layer(self, layer); + _add_layer(self, layer); // Shift everything right. for (size_t i = self->size; i > index; i--) { self->children[i] = self->children[i - 1]; } - self->children[index].native = native_layer; self->children[index].original = layer; self->size++; } @@ -299,7 +333,6 @@ mp_obj_t common_hal_displayio_group_pop(displayio_group_t* self, size_t index) { for (size_t i = index; i < self->size; i++) { self->children[i] = self->children[i + 1]; } - self->children[self->size].native = NULL; self->children[self->size].original = NULL; return item; } @@ -322,9 +355,8 @@ mp_obj_t common_hal_displayio_group_get(displayio_group_t* self, size_t index) { } void common_hal_displayio_group_set(displayio_group_t* self, size_t index, mp_obj_t layer) { - mp_obj_t native_layer = _add_layer(self, layer); + _add_layer(self, layer); _remove_layer(self, index); - self->children[index].native = native_layer; self->children[index].original = layer; } @@ -341,47 +373,61 @@ void displayio_group_construct(displayio_group_t* self, displayio_group_child_t* bool displayio_group_fill_area(displayio_group_t *self, const _displayio_colorspace_t* colorspace, const displayio_area_t* area, uint32_t* mask, uint32_t* buffer) { // Track if any of the layers finishes filling in the given area. We can ignore any remaining // layers at that point. - bool full_coverage = false; for (int32_t i = self->size - 1; i >= 0 ; i--) { - mp_obj_t layer = self->children[i].native; + mp_obj_t layer; #if CIRCUITPY_VECTORIO - if (MP_OBJ_IS_TYPE(layer, &vectorio_vector_shape_type)) { + layer = mp_instance_cast_to_native_base( + self->children[i].original, &vectorio_vector_shape_type); + if (layer != MP_OBJ_NULL) { if (vectorio_vector_shape_fill_area(layer, colorspace, area, mask, buffer)) { - full_coverage = true; - break; + return true; } + continue; } - else #endif - if (MP_OBJ_IS_TYPE(layer, &displayio_tilegrid_type)) { + layer = mp_instance_cast_to_native_base( + self->children[i].original, &displayio_tilegrid_type); + if (layer != MP_OBJ_NULL) { if (displayio_tilegrid_fill_area(layer, colorspace, area, mask, buffer)) { - full_coverage = true; - break; + return true; } - } else if (MP_OBJ_IS_TYPE(layer, &displayio_group_type)) { + continue; + } + layer = mp_instance_cast_to_native_base( + self->children[i].original, &displayio_group_type); + if (layer != MP_OBJ_NULL) { if (displayio_group_fill_area(layer, colorspace, area, mask, buffer)) { - full_coverage = true; - break; + return true; } + continue; } } - return full_coverage; + return false; } void displayio_group_finish_refresh(displayio_group_t *self) { self->item_removed = false; for (int32_t i = self->size - 1; i >= 0 ; i--) { - mp_obj_t layer = self->children[i].native; + mp_obj_t layer; #if CIRCUITPY_VECTORIO - if (MP_OBJ_IS_TYPE(layer, &vectorio_vector_shape_type)) { + layer = mp_instance_cast_to_native_base( + self->children[i].original, &vectorio_vector_shape_type); + if (layer != MP_OBJ_NULL) { vectorio_vector_shape_finish_refresh(layer); + continue; } - else #endif - if (MP_OBJ_IS_TYPE(layer, &displayio_tilegrid_type)) { + layer = mp_instance_cast_to_native_base( + self->children[i].original, &displayio_tilegrid_type); + if (layer != MP_OBJ_NULL) { displayio_tilegrid_finish_refresh(layer); - } else if (MP_OBJ_IS_TYPE(layer, &displayio_group_type)) { + continue; + } + layer = mp_instance_cast_to_native_base( + self->children[i].original, &displayio_group_type); + if (layer != MP_OBJ_NULL) { displayio_group_finish_refresh(layer); + continue; } } } @@ -393,17 +439,26 @@ displayio_area_t* displayio_group_get_refresh_areas(displayio_group_t *self, dis } for (int32_t i = self->size - 1; i >= 0 ; i--) { - mp_obj_t layer = self->children[i].native; + mp_obj_t layer; #if CIRCUITPY_VECTORIO - if (MP_OBJ_IS_TYPE(layer, &vectorio_vector_shape_type)) { + layer = mp_instance_cast_to_native_base( + self->children[i].original, &vectorio_vector_shape_type); + if (layer != MP_OBJ_NULL) { tail = vectorio_vector_shape_get_refresh_areas(layer, tail); + continue; } - else #endif - if (MP_OBJ_IS_TYPE(layer, &displayio_tilegrid_type)) { + layer = mp_instance_cast_to_native_base( + self->children[i].original, &displayio_tilegrid_type); + if (layer != MP_OBJ_NULL) { tail = displayio_tilegrid_get_refresh_areas(layer, tail); - } else if (MP_OBJ_IS_TYPE(layer, &displayio_group_type)) { + continue; + } + layer = mp_instance_cast_to_native_base( + self->children[i].original, &displayio_group_type); + if (layer != MP_OBJ_NULL) { tail = displayio_group_get_refresh_areas(layer, tail); + continue; } } diff --git a/shared-module/displayio/Group.h b/shared-module/displayio/Group.h index 5afaac1ba..9c2934aec 100644 --- a/shared-module/displayio/Group.h +++ b/shared-module/displayio/Group.h @@ -35,7 +35,6 @@ #include "shared-module/displayio/Palette.h" typedef struct { - mp_obj_t native; mp_obj_t original; } displayio_group_child_t; diff --git a/supervisor/shared/display.c b/supervisor/shared/display.c index 9c9c66cd7..a5830f764 100644 --- a/supervisor/shared/display.c +++ b/supervisor/shared/display.c @@ -272,13 +272,13 @@ displayio_tilegrid_t blinka_sprite = { #if CIRCUITPY_TERMINALIO #define CHILD_COUNT 2 displayio_group_child_t splash_children[2] = { - {&blinka_sprite, &blinka_sprite}, - {&supervisor_terminal_text_grid, &supervisor_terminal_text_grid} + {&blinka_sprite}, + {&supervisor_terminal_text_grid}, }; #else #define CHILD_COUNT 1 displayio_group_child_t splash_children[1] = { - {&blinka_sprite, &blinka_sprite}, + {&blinka_sprite}, }; #endif -- cgit v1.2.3 From 121c6bcc9b20d7419f2654680215fd8b4bd455c4 Mon Sep 17 00:00:00 2001 From: Radomir Dopieralski Date: Sun, 21 Feb 2021 13:43:28 +0100 Subject: Replace displaio.Group.children with a python list This is a first go at it, done by naive replacing of all array operations with corresponding operations on the list. Note that there is a lot of unnecessary type conversions, here. Also, list_pop has been copied, because it's decalerd STATIC in py/objlist.h --- shared-module/displayio/Group.c | 125 +++++++++++++++++++++------------------- shared-module/displayio/Group.h | 7 +-- supervisor/shared/display.c | 23 ++++---- 3 files changed, 82 insertions(+), 73 deletions(-) (limited to 'shared-module') diff --git a/shared-module/displayio/Group.c b/shared-module/displayio/Group.c index ba3bd4869..48f869489 100644 --- a/shared-module/displayio/Group.c +++ b/shared-module/displayio/Group.c @@ -33,10 +33,31 @@ #include "shared-bindings/vectorio/VectorShape.h" #endif +#include +#define LIST_MIN_ALLOC 4 + +STATIC mp_obj_t list_pop(size_t n_args, const mp_obj_t *args) { + mp_check_self(MP_OBJ_IS_TYPE(args[0], &mp_type_list)); + mp_obj_list_t *self = mp_instance_cast_to_native_base(args[0], &mp_type_list); + if (self->len == 0) { + mp_raise_IndexError_varg(translate("pop from empty %q"), MP_QSTR_list); + } + size_t index = mp_get_index(self->base.type, self->len, n_args == 1 ? MP_OBJ_NEW_SMALL_INT(-1) : args[1], false); + mp_obj_t ret = self->items[index]; + self->len -= 1; + memmove(self->items + index, self->items + index + 1, (self->len - index) * sizeof(mp_obj_t)); + // Clear stale pointer from slot which just got freed to prevent GC issues + self->items[self->len] = MP_OBJ_NULL; + if (self->alloc > LIST_MIN_ALLOC && self->alloc > 2 * self->len) { + self->items = m_renew(mp_obj_t, self->items, self->alloc, self->alloc/2); + self->alloc /= 2; + } + return ret; +} 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) { - displayio_group_child_t* children = m_new(displayio_group_child_t, max_size); - displayio_group_construct(self, children, max_size, scale, x, y); + mp_obj_list_t *members = mp_obj_new_list(0, NULL); + displayio_group_construct(self, members, scale, x, y); } bool common_hal_displayio_group_get_hidden(displayio_group_t* self) { @@ -51,16 +72,16 @@ void common_hal_displayio_group_set_hidden(displayio_group_t* self, bool hidden) if (self->hidden_by_parent) { return; } - for (size_t i = 0; i < self->size; i++) { + for (size_t i = 0; i < self->members->len; i++) { mp_obj_t layer; layer = mp_instance_cast_to_native_base( - self->children[i].original, &displayio_tilegrid_type); + self->members->items[i], &displayio_tilegrid_type); if (layer != MP_OBJ_NULL) { displayio_tilegrid_set_hidden_by_parent(layer, hidden); continue; } layer = mp_instance_cast_to_native_base( - self->children[i].original, &displayio_group_type); + self->members->items[i], &displayio_group_type); if (layer != MP_OBJ_NULL) { displayio_group_set_hidden_by_parent(layer, hidden); continue; @@ -77,16 +98,16 @@ void displayio_group_set_hidden_by_parent(displayio_group_t *self, bool hidden) if (self->hidden) { return; } - for (size_t i = 0; i < self->size; i++) { + for (size_t i = 0; i < self->members->len; i++) { mp_obj_t layer; layer = mp_instance_cast_to_native_base( - self->children[i].original, &displayio_tilegrid_type); + self->members->items[i], &displayio_tilegrid_type); if (layer != MP_OBJ_NULL) { displayio_tilegrid_set_hidden_by_parent(layer, hidden); continue; } layer = mp_instance_cast_to_native_base( - self->children[i].original, &displayio_group_type); + self->members->items[i], &displayio_group_type); if (layer != MP_OBJ_NULL) { displayio_group_set_hidden_by_parent(layer, hidden); continue; @@ -100,18 +121,18 @@ uint32_t common_hal_displayio_group_get_scale(displayio_group_t* self) { bool displayio_group_get_previous_area(displayio_group_t *self, displayio_area_t* area) { bool first = true; - for (size_t i = 0; i < self->size; i++) { + for (size_t i = 0; i < self->members->len; i++) { mp_obj_t layer; displayio_area_t layer_area; layer = mp_instance_cast_to_native_base( - self->children[i].original, &displayio_tilegrid_type); + self->members->items[i], &displayio_tilegrid_type); if (layer != MP_OBJ_NULL) { if (!displayio_tilegrid_get_previous_area(layer, &layer_area)) { continue; } } else { layer = mp_instance_cast_to_native_base( - self->children[i].original, &displayio_group_type); + self->members->items[i], &displayio_group_type); if (layer != MP_OBJ_NULL) { if (!displayio_group_get_previous_area(layer, &layer_area)) { continue; @@ -140,24 +161,24 @@ static void _update_child_transforms(displayio_group_t* self) { if (!self->in_group) { return; } - for (size_t i = 0; i < self->size; i++) { + for (size_t i = 0; i < self->members->len; i++) { mp_obj_t layer; #if CIRCUITPY_VECTORIO layer = mp_instance_cast_to_native_base( - self->children[i].original, &vectorio_vector_shape_type); + self->members->items[i], &vectorio_vector_shape_type); if (layer != MP_OBJ_NULL) { vectorio_vector_shape_update_transform(layer, &self->absolute_transform); continue; } #endif layer = mp_instance_cast_to_native_base( - self->children[i].original, &displayio_tilegrid_type); + self->members->items[i], &displayio_tilegrid_type); if (layer != MP_OBJ_NULL) { displayio_tilegrid_update_transform(layer, &self->absolute_transform); continue; } layer = mp_instance_cast_to_native_base( - self->children[i].original, &displayio_group_type); + self->members->items[i], &displayio_group_type); if (layer != MP_OBJ_NULL) { displayio_group_update_transform(layer, &self->absolute_transform); continue; @@ -279,7 +300,7 @@ static void _remove_layer(displayio_group_t* self, size_t index) { bool rendered_last_frame = false; #if CIRCUITPY_VECTORIO layer = mp_instance_cast_to_native_base( - self->children[index].original, &vectorio_vector_shape_type); + self->members->items[index], &vectorio_vector_shape_type); if (layer != MP_OBJ_NULL) { bool has_dirty_area = vectorio_vector_shape_get_dirty_area(layer, &layer_area); rendered_last_frame = has_dirty_area; @@ -287,14 +308,14 @@ static void _remove_layer(displayio_group_t* self, size_t index) { } #endif layer = mp_instance_cast_to_native_base( - self->children[index].original, &displayio_tilegrid_type); + self->members->items[index], &displayio_tilegrid_type); if (layer != MP_OBJ_NULL) { displayio_tilegrid_t* tilegrid = layer; rendered_last_frame = displayio_tilegrid_get_previous_area(tilegrid, &layer_area); displayio_tilegrid_update_transform(tilegrid, NULL); } layer = mp_instance_cast_to_native_base( - self->children[index].original, &displayio_group_type); + self->members->items[index], &displayio_group_type); if (layer != MP_OBJ_NULL) { displayio_group_t* group = layer; rendered_last_frame = displayio_group_get_previous_area(group, &layer_area); @@ -312,59 +333,45 @@ 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) { - if (self->size == self->max_size) { - mp_raise_RuntimeError(translate("Group full")); - } _add_layer(self, layer); - // Shift everything right. - for (size_t i = self->size; i > index; i--) { - self->children[i] = self->children[i - 1]; + 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->children[index].original = layer; - self->size++; + self->members->items[index] = layer; } mp_obj_t common_hal_displayio_group_pop(displayio_group_t* self, size_t index) { - self->size--; - mp_obj_t item = self->children[index].original; _remove_layer(self, index); - - // Shift everything left. - for (size_t i = index; i < self->size; i++) { - self->children[i] = self->children[i + 1]; - } - self->children[self->size].original = NULL; - return item; + mp_obj_t args[] = {self->members, MP_OBJ_NEW_SMALL_INT(index)}; + return list_pop(2, args); } mp_int_t common_hal_displayio_group_index(displayio_group_t* self, mp_obj_t layer) { - for (size_t i = 0; i < self->size; i++) { - if (self->children[i].original == layer) { - return i; - } - } - return -1; + mp_obj_t args[] = {self->members, layer}; + mp_obj_t *index = mp_seq_index_obj( + self->members->items, self->members->len, 2, args); + return MP_OBJ_SMALL_INT_VALUE(index); } size_t common_hal_displayio_group_get_len(displayio_group_t* self) { - return self->size; + return self->members->len; } mp_obj_t common_hal_displayio_group_get(displayio_group_t* self, size_t index) { - return self->children[index].original; + return self->members->items[index]; } void common_hal_displayio_group_set(displayio_group_t* self, size_t index, mp_obj_t layer) { _add_layer(self, layer); _remove_layer(self, index); - self->children[index].original = layer; + mp_obj_list_store(self, MP_OBJ_NEW_SMALL_INT(index), layer); } -void displayio_group_construct(displayio_group_t* self, displayio_group_child_t* child_array, uint32_t max_size, uint32_t scale, mp_int_t x, mp_int_t y) { +void displayio_group_construct(displayio_group_t* self, mp_obj_list_t* members, uint32_t scale, mp_int_t x, mp_int_t y) { self->x = x; self->y = y; - self->children = child_array; - self->max_size = max_size; + self->members = members; self->item_removed = false; self->scale = scale; self->in_group = false; @@ -373,11 +380,11 @@ void displayio_group_construct(displayio_group_t* self, displayio_group_child_t* bool displayio_group_fill_area(displayio_group_t *self, const _displayio_colorspace_t* colorspace, const displayio_area_t* area, uint32_t* mask, uint32_t* buffer) { // Track if any of the layers finishes filling in the given area. We can ignore any remaining // layers at that point. - for (int32_t i = self->size - 1; i >= 0 ; i--) { + for (int32_t i = self->members->len - 1; i >= 0 ; i--) { mp_obj_t layer; #if CIRCUITPY_VECTORIO layer = mp_instance_cast_to_native_base( - self->children[i].original, &vectorio_vector_shape_type); + self->members->items[i], &vectorio_vector_shape_type); if (layer != MP_OBJ_NULL) { if (vectorio_vector_shape_fill_area(layer, colorspace, area, mask, buffer)) { return true; @@ -386,7 +393,7 @@ bool displayio_group_fill_area(displayio_group_t *self, const _displayio_colorsp } #endif layer = mp_instance_cast_to_native_base( - self->children[i].original, &displayio_tilegrid_type); + self->members->items[i], &displayio_tilegrid_type); if (layer != MP_OBJ_NULL) { if (displayio_tilegrid_fill_area(layer, colorspace, area, mask, buffer)) { return true; @@ -394,7 +401,7 @@ bool displayio_group_fill_area(displayio_group_t *self, const _displayio_colorsp continue; } layer = mp_instance_cast_to_native_base( - self->children[i].original, &displayio_group_type); + self->members->items[i], &displayio_group_type); if (layer != MP_OBJ_NULL) { if (displayio_group_fill_area(layer, colorspace, area, mask, buffer)) { return true; @@ -407,24 +414,24 @@ bool displayio_group_fill_area(displayio_group_t *self, const _displayio_colorsp void displayio_group_finish_refresh(displayio_group_t *self) { self->item_removed = false; - for (int32_t i = self->size - 1; i >= 0 ; i--) { + for (int32_t i = self->members->len - 1; i >= 0 ; i--) { mp_obj_t layer; #if CIRCUITPY_VECTORIO layer = mp_instance_cast_to_native_base( - self->children[i].original, &vectorio_vector_shape_type); + self->members->items[i], &vectorio_vector_shape_type); if (layer != MP_OBJ_NULL) { vectorio_vector_shape_finish_refresh(layer); continue; } #endif layer = mp_instance_cast_to_native_base( - self->children[i].original, &displayio_tilegrid_type); + self->members->items[i], &displayio_tilegrid_type); if (layer != MP_OBJ_NULL) { displayio_tilegrid_finish_refresh(layer); continue; } layer = mp_instance_cast_to_native_base( - self->children[i].original, &displayio_group_type); + self->members->items[i], &displayio_group_type); if (layer != MP_OBJ_NULL) { displayio_group_finish_refresh(layer); continue; @@ -438,24 +445,24 @@ displayio_area_t* displayio_group_get_refresh_areas(displayio_group_t *self, dis tail = &self->dirty_area; } - for (int32_t i = self->size - 1; i >= 0 ; i--) { + for (int32_t i = self->members->len - 1; i >= 0 ; i--) { mp_obj_t layer; #if CIRCUITPY_VECTORIO layer = mp_instance_cast_to_native_base( - self->children[i].original, &vectorio_vector_shape_type); + self->members->items[i], &vectorio_vector_shape_type); if (layer != MP_OBJ_NULL) { tail = vectorio_vector_shape_get_refresh_areas(layer, tail); continue; } #endif layer = mp_instance_cast_to_native_base( - self->children[i].original, &displayio_tilegrid_type); + self->members->items[i], &displayio_tilegrid_type); if (layer != MP_OBJ_NULL) { tail = displayio_tilegrid_get_refresh_areas(layer, tail); continue; } layer = mp_instance_cast_to_native_base( - self->children[i].original, &displayio_group_type); + self->members->items[i], &displayio_group_type); if (layer != MP_OBJ_NULL) { tail = displayio_group_get_refresh_areas(layer, tail); continue; diff --git a/shared-module/displayio/Group.h b/shared-module/displayio/Group.h index 9c2934aec..b4054eb4a 100644 --- a/shared-module/displayio/Group.h +++ b/shared-module/displayio/Group.h @@ -31,6 +31,7 @@ #include #include "py/obj.h" +#include "py/objlist.h" #include "shared-module/displayio/area.h" #include "shared-module/displayio/Palette.h" @@ -40,14 +41,12 @@ typedef struct { typedef struct { mp_obj_base_t base; - displayio_group_child_t* children; + mp_obj_list_t *members; displayio_buffer_transform_t absolute_transform; displayio_area_t dirty_area; // Catch all for changed area int16_t x; int16_t y; uint16_t scale; - uint16_t size; - uint16_t max_size; bool item_removed :1; bool in_group :1; bool hidden :1; @@ -55,7 +54,7 @@ typedef struct { uint8_t padding :4; } displayio_group_t; -void displayio_group_construct(displayio_group_t* self, displayio_group_child_t* child_array, uint32_t max_size, uint32_t scale, mp_int_t x, mp_int_t y); +void displayio_group_construct(displayio_group_t* self, mp_obj_list_t* members, uint32_t scale, mp_int_t x, mp_int_t y); void displayio_group_set_hidden_by_parent(displayio_group_t *self, bool hidden); bool displayio_group_get_previous_area(displayio_group_t *group, displayio_area_t* area); bool displayio_group_fill_area(displayio_group_t *group, const _displayio_colorspace_t* colorspace, const displayio_area_t* area, uint32_t* mask, uint32_t *buffer); diff --git a/supervisor/shared/display.c b/supervisor/shared/display.c index a5830f764..1919cf4a4 100644 --- a/supervisor/shared/display.c +++ b/supervisor/shared/display.c @@ -270,15 +270,20 @@ displayio_tilegrid_t blinka_sprite = { }; #if CIRCUITPY_TERMINALIO -#define CHILD_COUNT 2 -displayio_group_child_t splash_children[2] = { - {&blinka_sprite}, - {&supervisor_terminal_text_grid}, +mp_obj_t members[] = { &blinka_sprite, &supervisor_terminal_text_grid, }; +mp_obj_list_t splash_children = { + .base = {.type = &mp_type_list }, + .alloc = 2, + .len = 2, + .items = members, }; #else -#define CHILD_COUNT 1 -displayio_group_child_t splash_children[1] = { - {&blinka_sprite}, +mp_obj_t members[] = { &blinka_sprite }; +mp_obj_list_t splash_children = { + .base = {.type = &mp_type_list }, + .alloc = 1, + .len = 1, + .items = members, }; #endif @@ -287,9 +292,7 @@ displayio_group_t circuitpython_splash = { .x = 0, .y = 0, .scale = 2, - .size = CHILD_COUNT, - .max_size = CHILD_COUNT, - .children = splash_children, + .members = &splash_children, .item_removed = false, .in_group = false, .hidden = false, -- cgit v1.2.3 From e505c59ed838095ccb2bd996fc77bd4aeada2d80 Mon Sep 17 00:00:00 2001 From: Radomir Dopieralski Date: Sun, 21 Feb 2021 16:25:17 +0100 Subject: Separate mp_obj_list_pop so it can be used outside of objlist.c --- py/objlist.c | 12 ++++++++---- py/objlist.h | 1 + shared-module/displayio/Group.c | 25 ++----------------------- 3 files changed, 11 insertions(+), 27 deletions(-) (limited to 'shared-module') diff --git a/py/objlist.c b/py/objlist.c index 51ec920be..309e0e542 100644 --- a/py/objlist.c +++ b/py/objlist.c @@ -270,13 +270,10 @@ 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 } -STATIC mp_obj_t list_pop(size_t n_args, const mp_obj_t *args) { - mp_check_self(MP_OBJ_IS_TYPE(args[0], &mp_type_list)); - mp_obj_list_t *self = mp_instance_cast_to_native_base(args[0], &mp_type_list); +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); } - size_t index = mp_get_index(self->base.type, self->len, n_args == 1 ? MP_OBJ_NEW_SMALL_INT(-1) : args[1], false); mp_obj_t ret = self->items[index]; self->len -= 1; memmove(self->items + index, self->items + index + 1, (self->len - index) * sizeof(mp_obj_t)); @@ -289,6 +286,13 @@ STATIC mp_obj_t list_pop(size_t n_args, const mp_obj_t *args) { return ret; } +STATIC mp_obj_t list_pop(size_t n_args, const mp_obj_t *args) { + mp_check_self(MP_OBJ_IS_TYPE(args[0], &mp_type_list)); + mp_obj_list_t *self = mp_instance_cast_to_native_base(args[0], &mp_type_list); + size_t index = mp_get_index(self->base.type, self->len, n_args == 1 ? MP_OBJ_NEW_SMALL_INT(-1) : args[1], false); + return mp_obj_list_pop(self, index); +} + STATIC void mp_quicksort(mp_obj_t *head, mp_obj_t *tail, mp_obj_t key_fn, mp_obj_t binop_less_result) { MP_STACK_CHECK(); while (head < tail) { diff --git a/py/objlist.h b/py/objlist.h index f02030557..cec23e06c 100644 --- a/py/objlist.h +++ b/py/objlist.h @@ -36,5 +36,6 @@ typedef struct _mp_obj_list_t { } 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); #endif // MICROPY_INCLUDED_PY_OBJLIST_H diff --git a/shared-module/displayio/Group.c b/shared-module/displayio/Group.c index 48f869489..97bb86c9a 100644 --- a/shared-module/displayio/Group.c +++ b/shared-module/displayio/Group.c @@ -27,33 +27,13 @@ #include "shared-bindings/displayio/Group.h" #include "py/runtime.h" +#include "py/objlist.h" #include "shared-bindings/displayio/TileGrid.h" #if CIRCUITPY_VECTORIO #include "shared-bindings/vectorio/VectorShape.h" #endif -#include -#define LIST_MIN_ALLOC 4 - -STATIC mp_obj_t list_pop(size_t n_args, const mp_obj_t *args) { - mp_check_self(MP_OBJ_IS_TYPE(args[0], &mp_type_list)); - mp_obj_list_t *self = mp_instance_cast_to_native_base(args[0], &mp_type_list); - if (self->len == 0) { - mp_raise_IndexError_varg(translate("pop from empty %q"), MP_QSTR_list); - } - size_t index = mp_get_index(self->base.type, self->len, n_args == 1 ? MP_OBJ_NEW_SMALL_INT(-1) : args[1], false); - mp_obj_t ret = self->items[index]; - self->len -= 1; - memmove(self->items + index, self->items + index + 1, (self->len - index) * sizeof(mp_obj_t)); - // Clear stale pointer from slot which just got freed to prevent GC issues - self->items[self->len] = MP_OBJ_NULL; - if (self->alloc > LIST_MIN_ALLOC && self->alloc > 2 * self->len) { - self->items = m_renew(mp_obj_t, self->items, self->alloc, self->alloc/2); - self->alloc /= 2; - } - return ret; -} 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) { mp_obj_list_t *members = mp_obj_new_list(0, NULL); @@ -343,8 +323,7 @@ void common_hal_displayio_group_insert(displayio_group_t* self, size_t index, mp mp_obj_t common_hal_displayio_group_pop(displayio_group_t* self, size_t index) { _remove_layer(self, index); - mp_obj_t args[] = {self->members, MP_OBJ_NEW_SMALL_INT(index)}; - return list_pop(2, args); + return mp_obj_list_pop(self->members, index); } mp_int_t common_hal_displayio_group_index(displayio_group_t* self, mp_obj_t layer) { -- cgit v1.2.3 From 9c41753e44b72f1728931ffb6688d9327da8a8ae Mon Sep 17 00:00:00 2001 From: Radomir Dopieralski Date: Tue, 23 Feb 2021 19:40:22 +0100 Subject: Remove unused typedef for group children --- shared-module/displayio/Group.h | 4 ---- 1 file changed, 4 deletions(-) (limited to 'shared-module') diff --git a/shared-module/displayio/Group.h b/shared-module/displayio/Group.h index b4054eb4a..0964e0a0b 100644 --- a/shared-module/displayio/Group.h +++ b/shared-module/displayio/Group.h @@ -35,10 +35,6 @@ #include "shared-module/displayio/area.h" #include "shared-module/displayio/Palette.h" -typedef struct { - mp_obj_t original; -} displayio_group_child_t; - typedef struct { mp_obj_base_t base; mp_obj_list_t *members; -- 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-module') 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-module') 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