diff options
| author | jgillick <j_gillick@yahoo.com> | 2020-11-17 00:02:24 -0800 |
|---|---|---|
| committer | jgillick <j_gillick@yahoo.com> | 2020-11-17 00:02:24 -0800 |
| commit | 5f0a372a2206c14e4ae206ec7cb879265aa7a38a (patch) | |
| tree | 7d0e5fd15aedbf28c5fd5a8d685928ed23146b7d /shared-module | |
| parent | 119e9d38202cb26f7f04d1050263e27e1083865a (diff) | |
| parent | 42ca57ff8f19368dff97bebd9ca0ac061b219084 (diff) | |
Merge tag '6.0.0' into thunderpack1.2
Diffstat (limited to 'shared-module')
88 files changed, 3962 insertions, 496 deletions
diff --git a/shared-module/_bleio/Characteristic.h b/shared-module/_bleio/Characteristic.h index 409a57c76..7776b1fa5 100644 --- a/shared-module/_bleio/Characteristic.h +++ b/shared-module/_bleio/Characteristic.h @@ -27,6 +27,9 @@ #ifndef MICROPY_INCLUDED_SHARED_MODULE_BLEIO_CHARACTERISTIC_H #define MICROPY_INCLUDED_SHARED_MODULE_BLEIO_CHARACTERISTIC_H +// These are not the Bluetooth spec values. They are what is used by the Nordic SoftDevice. +// The bit values are in different positions. + typedef enum { CHAR_PROP_NONE = 0, CHAR_PROP_BROADCAST = 1u << 0, @@ -40,4 +43,14 @@ typedef enum { } bleio_characteristic_properties_enum_t; typedef uint8_t bleio_characteristic_properties_t; +// Bluetooth spec property values +#define BT_GATT_CHRC_BROADCAST 0x01 +#define BT_GATT_CHRC_READ 0x02 +#define BT_GATT_CHRC_WRITE_WITHOUT_RESP 0x04 +#define BT_GATT_CHRC_WRITE 0x08 +#define BT_GATT_CHRC_NOTIFY 0x10 +#define BT_GATT_CHRC_INDICATE 0x20 +#define BT_GATT_CHRC_AUTH 0x40 +#define BT_GATT_CHRC_EXT_PROP 0x80 + #endif // MICROPY_INCLUDED_SHARED_MODULE_BLEIO_CHARACTERISTIC_H diff --git a/shared-module/_bleio/ScanEntry.c b/shared-module/_bleio/ScanEntry.c index 785209c4a..af9e4b347 100644 --- a/shared-module/_bleio/ScanEntry.c +++ b/shared-module/_bleio/ScanEntry.c @@ -56,11 +56,16 @@ bool bleio_scanentry_data_matches(const uint8_t* data, size_t len, const uint8_t if (prefixes_length == 0) { return true; } + if (len == 0) { + // Prefixes exist, but no data. + return false; + } size_t i = 0; while(i < prefixes_length) { uint8_t prefix_length = prefixes[i]; i += 1; size_t j = 0; + bool prefix_matched = false; while (j < len) { uint8_t structure_length = data[j]; j += 1; @@ -71,13 +76,18 @@ bool bleio_scanentry_data_matches(const uint8_t* data, size_t len, const uint8_t if (any) { return true; } - } else if (!any) { - return false; + prefix_matched = true; + break; } j += structure_length; } + // If all (!any), the current prefix must have matched at least one field. + if (!prefix_matched && !any) { + return false; + } i += prefix_length; } + // All prefixes matched some field (if !any), or none did (if any). return !any; } diff --git a/shared-module/_bleio/ScanResults.c b/shared-module/_bleio/ScanResults.c index 7ea0c165f..cb48d636d 100644 --- a/shared-module/_bleio/ScanResults.c +++ b/shared-module/_bleio/ScanResults.c @@ -45,10 +45,10 @@ bleio_scanresults_obj_t* shared_module_bleio_new_scanresults(size_t buffer_size, } mp_obj_t common_hal_bleio_scanresults_next(bleio_scanresults_obj_t *self) { - while (ringbuf_count(&self->buf) == 0 && !self->done && !mp_hal_is_interrupted()) { + while (ringbuf_num_filled(&self->buf) == 0 && !self->done && !mp_hal_is_interrupted()) { RUN_BACKGROUND_TASKS; } - if (ringbuf_count(&self->buf) == 0 || mp_hal_is_interrupted()) { + if (ringbuf_num_filled(&self->buf) == 0 || mp_hal_is_interrupted()) { return mp_const_none; } @@ -56,7 +56,7 @@ mp_obj_t common_hal_bleio_scanresults_next(bleio_scanresults_obj_t *self) { uint8_t type = ringbuf_get(&self->buf); bool connectable = (type & (1 << 0)) != 0; bool scan_response = (type & (1 << 1)) != 0; - uint64_t ticks_ms; + uint64_t ticks_ms; ringbuf_get_n(&self->buf, (uint8_t*) &ticks_ms, sizeof(ticks_ms)); uint8_t rssi = ringbuf_get(&self->buf); uint8_t peer_addr[NUM_BLEIO_ADDRESS_BYTES]; @@ -81,7 +81,7 @@ mp_obj_t common_hal_bleio_scanresults_next(bleio_scanresults_obj_t *self) { entry->time_received = ticks_ms; entry->connectable = connectable; entry->scan_response = scan_response; - + return MP_OBJ_FROM_PTR(entry); } @@ -97,7 +97,7 @@ void shared_module_bleio_scanresults_append(bleio_scanresults_obj_t* self, uint16_t len) { int32_t packet_size = sizeof(uint8_t) + sizeof(ticks_ms) + sizeof(rssi) + NUM_BLEIO_ADDRESS_BYTES + sizeof(addr_type) + sizeof(len) + len; - int32_t empty_space = self->buf.size - ringbuf_count(&self->buf); + int32_t empty_space = self->buf.size - ringbuf_num_filled(&self->buf); if (packet_size >= empty_space) { // We can't fit the packet so skip it. return; diff --git a/shared-module/_eve/__init__.c b/shared-module/_eve/__init__.c index 2c93eb69f..0f1e12d9f 100644 --- a/shared-module/_eve/__init__.c +++ b/shared-module/_eve/__init__.c @@ -314,4 +314,3 @@ void common_hal__eve_VertexTranslateY(common_hal__eve_t *eve, uint32_t y) { void common_hal__eve_Vertex2ii(common_hal__eve_t *eve, uint32_t x, uint32_t y, uint32_t handle, uint32_t cell) { C4(eve, ((2 << 30) | (((x) & 511) << 21) | (((y) & 511) << 12) | (((handle) & 31) << 7) | (((cell) & 127) << 0))); } - diff --git a/shared-module/_pew/PewPew.c b/shared-module/_pew/PewPew.c deleted file mode 100644 index 568dc6425..000000000 --- a/shared-module/_pew/PewPew.c +++ /dev/null @@ -1,125 +0,0 @@ -/* - * This file is part of the MicroPython project, http://micropython.org/ - * - * The MIT License (MIT) - * - * Copyright (c) 2019 Radomir Dopieralski - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -#include <stdbool.h> - -#include "py/mpstate.h" -#include "py/runtime.h" -#include "__init__.h" -#include "PewPew.h" - -#include "shared-bindings/digitalio/Pull.h" -#include "shared-bindings/digitalio/DigitalInOut.h" -#include "shared-bindings/util.h" -#include "samd/timers.h" -#include "supervisor/shared/translate.h" -#include "timer_handler.h" - - -static uint8_t pewpew_tc_index = 0xff; - - -void pewpew_interrupt_handler(uint8_t index) { - if (index != pewpew_tc_index) { - return; - } - Tc* tc = tc_insts[index]; - if (!tc->COUNT16.INTFLAG.bit.MC0) { - return; - } - - pew_tick(); - - // Clear the interrupt bit. - tc->COUNT16.INTFLAG.reg = TC_INTFLAG_MC0; -} - -void pew_init() { - pew_obj_t* pew = MP_STATE_VM(pew_singleton); - - common_hal_digitalio_digitalinout_switch_to_input(pew->buttons, PULL_UP); - - for (size_t i = 0; i < pew->rows_size; ++i) { - digitalio_digitalinout_obj_t *pin = MP_OBJ_TO_PTR(pew->rows[i]); - common_hal_digitalio_digitalinout_switch_to_output(pin, false, - DRIVE_MODE_PUSH_PULL); - } - for (size_t i = 0; i < pew->cols_size; ++i) { - digitalio_digitalinout_obj_t *pin = MP_OBJ_TO_PTR(pew->cols[i]); - common_hal_digitalio_digitalinout_switch_to_output(pin, true, - DRIVE_MODE_OPEN_DRAIN); - } - if (pewpew_tc_index == 0xff) { - // Find a spare timer. - uint8_t index = find_free_timer(); - if (index == 0xff) { - mp_raise_RuntimeError(translate("All timers in use")); - } - Tc *tc = tc_insts[index]; - - pewpew_tc_index = index; - set_timer_handler(true, index, TC_HANDLER_PEW); - - // We use GCLK0 for SAMD21 and GCLK1 for SAMD51 because they both run - // at 48mhz making our math the same across the boards. - #ifdef SAMD21 - turn_on_clocks(true, index, 0); - #endif - #ifdef SAMD51 - turn_on_clocks(true, index, 1); - #endif - - - #ifdef SAMD21 - tc->COUNT16.CTRLA.reg = TC_CTRLA_MODE_COUNT16 | - TC_CTRLA_PRESCALER_DIV64 | - TC_CTRLA_WAVEGEN_MFRQ; - #endif - #ifdef SAMD51 - tc_reset(tc); - tc_set_enable(tc, false); - tc->COUNT16.CTRLA.reg = TC_CTRLA_MODE_COUNT16 - | TC_CTRLA_PRESCALER_DIV64; - tc->COUNT16.WAVE.reg = TC_WAVE_WAVEGEN_MFRQ; - #endif - - tc_set_enable(tc, true); - tc->COUNT16.CC[0].reg = 64; - - // Clear our interrupt in case it was set earlier - tc->COUNT16.INTFLAG.reg = TC_INTFLAG_MC0; - tc->COUNT16.INTENSET.reg = TC_INTENSET_MC0; - tc_enable_interrupts(pewpew_tc_index); - } -} - -void pew_reset(void) { - if (pewpew_tc_index != 0xff) { - tc_reset(tc_insts[pewpew_tc_index]); - pewpew_tc_index = 0xff; - } - MP_STATE_VM(pew_singleton) = NULL; -} diff --git a/shared-module/_pew/__init__.c b/shared-module/_pew/__init__.c deleted file mode 100644 index f3b23c606..000000000 --- a/shared-module/_pew/__init__.c +++ /dev/null @@ -1,87 +0,0 @@ -/* - * This file is part of the MicroPython project, http://micropython.org/ - * - * The MIT License (MIT) - * - * Copyright (c) 2019 Radomir Dopieralski - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -#include <stdbool.h> - -#include "py/mpstate.h" -#include "__init__.h" -#include "PewPew.h" - -#include "shared-bindings/digitalio/DigitalInOut.h" - - -void pew_tick(void) { - static uint8_t col = 0; - static uint8_t turn = 0; - static uint8_t pressed = 0; - static uint8_t last_pressed = 0; - digitalio_digitalinout_obj_t *pin; - - pew_obj_t* pew = MP_STATE_VM(pew_singleton); - if (!pew) { return; } - - pin = MP_OBJ_TO_PTR(pew->cols[col]); - ++col; - if (col >= pew->cols_size) { - pew->pressed |= last_pressed & pressed; - last_pressed = pressed; - pressed = 0; - col = 0; - ++turn; - if (turn > 11) { - turn = 0; - } - } - if (!common_hal_digitalio_digitalinout_get_value(pew->buttons)) { - pressed |= 1 << col; - } - common_hal_digitalio_digitalinout_set_value(pin, true); - for (size_t x = 0; x < pew->rows_size; ++x) { - pin = MP_OBJ_TO_PTR(pew->rows[x]); - uint8_t color = pew->buffer[col * (pew->rows_size) + x]; - bool value = false; - switch (color & 0x03) { - case 3: - value = true; - break; - case 2: - if (turn == 2 || turn == 5 || turn == 8 || turn == 11) { - value = true; - } - break; - case 1: - if (turn == 0) { - value = true; - } - break; - case 0: - break; - } - common_hal_digitalio_digitalinout_set_value(pin, value); - } - pin = MP_OBJ_TO_PTR(pew->cols[col]); - common_hal_digitalio_digitalinout_set_value(pin, false); -} diff --git a/shared-module/_pixelbuf/PixelBuf.c b/shared-module/_pixelbuf/PixelBuf.c index 31350b875..f3e679631 100644 --- a/shared-module/_pixelbuf/PixelBuf.c +++ b/shared-module/_pixelbuf/PixelBuf.c @@ -132,6 +132,18 @@ void common_hal__pixelbuf_pixelbuf_set_brightness(mp_obj_t self_in, mp_float_t b } } +uint8_t _pixelbuf_get_as_uint8(mp_obj_t obj) { + if (MP_OBJ_IS_SMALL_INT(obj)) { + return MP_OBJ_SMALL_INT_VALUE(obj); + } else if (MP_OBJ_IS_INT(obj)) { + return mp_obj_get_int_truncated(obj); + } else if (mp_obj_is_float(obj)) { + return (uint8_t)mp_obj_get_float(obj); + } + mp_raise_TypeError_varg( + translate("can't convert %q to %q"), mp_obj_get_type_qstr(obj), MP_QSTR_int); +} + void _pixelbuf_parse_color(pixelbuf_pixelbuf_obj_t* self, mp_obj_t color, uint8_t* r, uint8_t* g, uint8_t* b, uint8_t* w) { pixelbuf_byteorder_details_t *byteorder = &self->byteorder; // w is shared between white in NeoPixels and brightness in dotstars (so that DotStars can have @@ -142,37 +154,39 @@ void _pixelbuf_parse_color(pixelbuf_pixelbuf_obj_t* self, mp_obj_t color, uint8_ *w = 0; } - if (MP_OBJ_IS_INT(color)) { - mp_int_t value = mp_obj_get_int_truncated(color); + if (MP_OBJ_IS_INT(color) || mp_obj_is_float(color)) { + mp_int_t value = MP_OBJ_IS_INT(color) ? mp_obj_get_int_truncated(color) : mp_obj_get_float(color); *r = value >> 16 & 0xff; *g = (value >> 8) & 0xff; *b = value & 0xff; - // Int colors can't set white directly so convert to white when all components are equal. - if (!byteorder->is_dotstar && byteorder->bpp == 4 && byteorder->has_white && *r == *g && *r == *b) { - *w = *r; - *r = 0; - *g = 0; - *b = 0; - } } else { mp_obj_t *items; size_t len; mp_obj_get_array(color, &len, &items); - if (len != byteorder->bpp && !byteorder->is_dotstar) { + if (len < 3 || len > 4) { mp_raise_ValueError_varg(translate("Expected tuple of length %d, got %d"), byteorder->bpp, len); } - *r = mp_obj_get_int_truncated(items[PIXEL_R]); - *g = mp_obj_get_int_truncated(items[PIXEL_G]); - *b = mp_obj_get_int_truncated(items[PIXEL_B]); + *r = _pixelbuf_get_as_uint8(items[PIXEL_R]); + *g = _pixelbuf_get_as_uint8(items[PIXEL_G]); + *b = _pixelbuf_get_as_uint8(items[PIXEL_B]); if (len > 3) { if (mp_obj_is_float(items[PIXEL_W])) { *w = 255 * mp_obj_get_float(items[PIXEL_W]); } else { *w = mp_obj_get_int_truncated(items[PIXEL_W]); } + return; } } + // Int colors can't set white directly so convert to white when all components are equal. + // Also handles RGBW values assigned an RGB tuple. + if (!byteorder->is_dotstar && byteorder->bpp == 4 && byteorder->has_white && *r == *g && *r == *b) { + *w = *r; + *r = 0; + *g = 0; + *b = 0; + } } void _pixelbuf_set_pixel_color(pixelbuf_pixelbuf_obj_t* self, size_t index, uint8_t r, uint8_t g, uint8_t b, uint8_t w) { @@ -216,18 +230,35 @@ void _pixelbuf_set_pixel(pixelbuf_pixelbuf_obj_t* self, size_t index, mp_obj_t v _pixelbuf_set_pixel_color(self, index, r, g, b, w); } -void common_hal__pixelbuf_pixelbuf_set_pixels(mp_obj_t self_in, size_t start, size_t stop, size_t step, mp_obj_t* values) { +void common_hal__pixelbuf_pixelbuf_set_pixels(mp_obj_t self_in, size_t start, mp_int_t step, size_t slice_len, mp_obj_t* values, + mp_obj_tuple_t *flatten_to) +{ pixelbuf_pixelbuf_obj_t* self = native_pixelbuf(self_in); - size_t source_i = 0; - for (size_t target_i = start; target_i < stop; target_i += step) { - _pixelbuf_set_pixel(self, target_i, values[source_i]); - source_i++; + mp_obj_iter_buf_t iter_buf; + mp_obj_t iterable = mp_getiter(values, &iter_buf); + mp_obj_t item; + size_t i = 0; + bool flattened = flatten_to != mp_const_none; + if (flattened) flatten_to->len = self->bytes_per_pixel; + while ((item = mp_iternext(iterable)) != MP_OBJ_STOP_ITERATION) { + if (flattened) { + flatten_to->items[i % self->bytes_per_pixel] = item; + if (++i % self->bytes_per_pixel == 0) { + _pixelbuf_set_pixel(self, start, flatten_to); + start+=step; + } + } else { + _pixelbuf_set_pixel(self, start, item); + start+=step; + } } if (self->auto_write) { common_hal__pixelbuf_pixelbuf_show(self_in); } } + + void common_hal__pixelbuf_pixelbuf_set_pixel(mp_obj_t self_in, size_t index, mp_obj_t value) { pixelbuf_pixelbuf_obj_t* self = native_pixelbuf(self_in); _pixelbuf_set_pixel(self, index, value); diff --git a/shared-module/_stage/__init__.c b/shared-module/_stage/__init__.c index 0323c32cb..6dfc18880 100644 --- a/shared-module/_stage/__init__.c +++ b/shared-module/_stage/__init__.c @@ -45,7 +45,7 @@ void render_stage(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, area.y2 = y1; displayio_display_core_set_region_to_update( &display->core, display->set_column_command, display->set_row_command, - NO_COMMAND, NO_COMMAND, display->data_as_commands, false, &area); + NO_COMMAND, NO_COMMAND, display->data_as_commands, false, &area, display->SH1107_addressing); while (!displayio_display_core_begin_transaction(&display->core)) { RUN_BACKGROUND_TASKS; diff --git a/shared-module/aesio/__init__.c b/shared-module/aesio/__init__.c new file mode 100644 index 000000000..2cacaeb66 --- /dev/null +++ b/shared-module/aesio/__init__.c @@ -0,0 +1,58 @@ +#include <string.h> + +#include "py/runtime.h" + +#include "shared-bindings/aesio/__init__.h" +#include "shared-module/aesio/__init__.h" + +void common_hal_aesio_aes_construct(aesio_aes_obj_t *self, const uint8_t *key, + uint32_t key_length, const uint8_t *iv, + int mode, int counter) { + self->mode = mode; + self->counter = counter; + common_hal_aesio_aes_rekey(self, key, key_length, iv); +} + +void common_hal_aesio_aes_rekey(aesio_aes_obj_t *self, const uint8_t *key, + uint32_t key_length, const uint8_t *iv) { + memset(&self->ctx, 0, sizeof(self->ctx)); + if (iv != NULL) { + AES_init_ctx_iv(&self->ctx, key, key_length, iv); + } else { + AES_init_ctx(&self->ctx, key, key_length); + } +} + +void common_hal_aesio_aes_set_mode(aesio_aes_obj_t *self, int mode) { + self->mode = mode; +} + +void common_hal_aesio_aes_encrypt(aesio_aes_obj_t *self, uint8_t *buffer, + size_t length) { + switch (self->mode) { + case AES_MODE_ECB: + AES_ECB_encrypt(&self->ctx, buffer); + break; + case AES_MODE_CBC: + AES_CBC_encrypt_buffer(&self->ctx, buffer, length); + break; + case AES_MODE_CTR: + AES_CTR_xcrypt_buffer(&self->ctx, buffer, length); + break; + } +} + +void common_hal_aesio_aes_decrypt(aesio_aes_obj_t *self, uint8_t *buffer, + size_t length) { + switch (self->mode) { + case AES_MODE_ECB: + AES_ECB_decrypt(&self->ctx, buffer); + break; + case AES_MODE_CBC: + AES_CBC_decrypt_buffer(&self->ctx, buffer, length); + break; + case AES_MODE_CTR: + AES_CTR_xcrypt_buffer(&self->ctx, buffer, length); + break; + } +} diff --git a/shared-module/aesio/__init__.h b/shared-module/aesio/__init__.h new file mode 100644 index 000000000..1a0bb8696 --- /dev/null +++ b/shared-module/aesio/__init__.h @@ -0,0 +1,59 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2018 Dan Halbert for Adafruit Industries + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifndef MICROPY_INCLUDED_SHARED_MODULE_AESIO__INIT__H +#define MICROPY_INCLUDED_SHARED_MODULE_AESIO__INIT__H + +#include <stdbool.h> +#include <stdint.h> + +#include "py/obj.h" +#include "py/proto.h" + +#include "shared-module/aesio/aes.h" + +// These values were chosen to correspond with the values +// present in pycrypto. +enum AES_MODE { + AES_MODE_ECB = 1, + AES_MODE_CBC = 2, + AES_MODE_CTR = 6, +}; + +typedef struct { + mp_obj_base_t base; + + // The tinyaes context + struct AES_ctx ctx; + + // Which AES mode this instance of the object is configured to use + enum AES_MODE mode; + + // Counter for running in CTR mode + uint32_t counter; +} aesio_aes_obj_t; + +#endif // MICROPY_INCLUDED_SHARED_MODULE_AESIO__INIT__H diff --git a/shared-module/aesio/aes.c b/shared-module/aesio/aes.c new file mode 100644 index 000000000..b62b5afc4 --- /dev/null +++ b/shared-module/aesio/aes.c @@ -0,0 +1,608 @@ +/* + +This is an implementation of the AES algorithm, specifically ECB, CTR and CBC mode. +Block size can be chosen in aes.h - available choices are AES128, AES192, AES256. + +The implementation is verified against the test vectors in: + National Institute of Standards and Technology Special Publication 800-38A 2001 ED + +ECB-AES128 +---------- + + plain-text: + 6bc1bee22e409f96e93d7e117393172a + ae2d8a571e03ac9c9eb76fac45af8e51 + 30c81c46a35ce411e5fbc1191a0a52ef + f69f2445df4f9b17ad2b417be66c3710 + + key: + 2b7e151628aed2a6abf7158809cf4f3c + + resulting cipher + 3ad77bb40d7a3660a89ecaf32466ef97 + f5d3d58503b9699de785895a96fdbaaf + 43b1cd7f598ece23881b00e3ed030688 + 7b0c785e27e8ad3f8223207104725dd4 + + +NOTE: String length must be evenly divisible by 16byte (str_len % 16 == 0) + You should pad the end of the string with zeros if this is not the case. + For AES192/256 the key size is proportionally larger. + +*/ + +/*****************************************************************************/ +/* Includes: */ +/*****************************************************************************/ +#include <string.h> // CBC mode, for memset +#include "aes.h" + +/*****************************************************************************/ +/* Defines: */ +/*****************************************************************************/ +// The number of columns comprising a state in AES. This is a constant in AES. +// Value=4 +#define Nb 4UL + +#if defined(AES256) && (AES256 == 1) + #define Nk256 8UL + #define Nr256 14UL +#endif +#if defined(AES192) && (AES192 == 1) + #define Nk192 6UL + #define Nr192 12UL +#endif +#if defined(AES128) && (AES128 == 1) + #define Nk128 4UL // The number of 32 bit words in a key. + #define Nr128 10UL // The number of rounds in AES Cipher. +#endif + +// jcallan@github points out that declaring Multiply as a function reduces code +// size considerably with the Keil ARM compiler. See this link for more +// information: https://github.com/kokke/tiny-AES-C/pull/3 +#ifndef MULTIPLY_AS_A_FUNCTION + #define MULTIPLY_AS_A_FUNCTION 0 +#endif + + + + +/*****************************************************************************/ +/* Private variables: */ +/*****************************************************************************/ +// state - array holding the intermediate results during decryption. +typedef uint8_t state_t[4][4]; + + + +// The lookup-tables are marked const so they can be placed in read-only storage +// instead of RAM The numbers below can be computed dynamically trading ROM for +// RAM - This can be useful in (embedded) bootloader applications, where ROM is +// often limited. +static const uint8_t sbox[256] = { + //0 1 2 3 4 5 6 7 8 9 A B C D E F + 0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, 0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76, + 0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0, 0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0, + 0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc, 0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15, + 0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a, 0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75, + 0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0, 0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84, + 0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b, 0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf, + 0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85, 0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8, + 0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5, 0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2, + 0xcd, 0x0c, 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17, 0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73, + 0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88, 0x46, 0xee, 0xb8, 0x14, 0xde, 0x5e, 0x0b, 0xdb, + 0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c, 0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79, + 0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, 0x4e, 0xa9, 0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08, + 0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6, 0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a, + 0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e, 0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e, + 0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94, 0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf, + 0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68, 0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16 }; + +static const uint8_t rsbox[256] = { + 0x52, 0x09, 0x6a, 0xd5, 0x30, 0x36, 0xa5, 0x38, 0xbf, 0x40, 0xa3, 0x9e, 0x81, 0xf3, 0xd7, 0xfb, + 0x7c, 0xe3, 0x39, 0x82, 0x9b, 0x2f, 0xff, 0x87, 0x34, 0x8e, 0x43, 0x44, 0xc4, 0xde, 0xe9, 0xcb, + 0x54, 0x7b, 0x94, 0x32, 0xa6, 0xc2, 0x23, 0x3d, 0xee, 0x4c, 0x95, 0x0b, 0x42, 0xfa, 0xc3, 0x4e, + 0x08, 0x2e, 0xa1, 0x66, 0x28, 0xd9, 0x24, 0xb2, 0x76, 0x5b, 0xa2, 0x49, 0x6d, 0x8b, 0xd1, 0x25, + 0x72, 0xf8, 0xf6, 0x64, 0x86, 0x68, 0x98, 0x16, 0xd4, 0xa4, 0x5c, 0xcc, 0x5d, 0x65, 0xb6, 0x92, + 0x6c, 0x70, 0x48, 0x50, 0xfd, 0xed, 0xb9, 0xda, 0x5e, 0x15, 0x46, 0x57, 0xa7, 0x8d, 0x9d, 0x84, + 0x90, 0xd8, 0xab, 0x00, 0x8c, 0xbc, 0xd3, 0x0a, 0xf7, 0xe4, 0x58, 0x05, 0xb8, 0xb3, 0x45, 0x06, + 0xd0, 0x2c, 0x1e, 0x8f, 0xca, 0x3f, 0x0f, 0x02, 0xc1, 0xaf, 0xbd, 0x03, 0x01, 0x13, 0x8a, 0x6b, + 0x3a, 0x91, 0x11, 0x41, 0x4f, 0x67, 0xdc, 0xea, 0x97, 0xf2, 0xcf, 0xce, 0xf0, 0xb4, 0xe6, 0x73, + 0x96, 0xac, 0x74, 0x22, 0xe7, 0xad, 0x35, 0x85, 0xe2, 0xf9, 0x37, 0xe8, 0x1c, 0x75, 0xdf, 0x6e, + 0x47, 0xf1, 0x1a, 0x71, 0x1d, 0x29, 0xc5, 0x89, 0x6f, 0xb7, 0x62, 0x0e, 0xaa, 0x18, 0xbe, 0x1b, + 0xfc, 0x56, 0x3e, 0x4b, 0xc6, 0xd2, 0x79, 0x20, 0x9a, 0xdb, 0xc0, 0xfe, 0x78, 0xcd, 0x5a, 0xf4, + 0x1f, 0xdd, 0xa8, 0x33, 0x88, 0x07, 0xc7, 0x31, 0xb1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xec, 0x5f, + 0x60, 0x51, 0x7f, 0xa9, 0x19, 0xb5, 0x4a, 0x0d, 0x2d, 0xe5, 0x7a, 0x9f, 0x93, 0xc9, 0x9c, 0xef, + 0xa0, 0xe0, 0x3b, 0x4d, 0xae, 0x2a, 0xf5, 0xb0, 0xc8, 0xeb, 0xbb, 0x3c, 0x83, 0x53, 0x99, 0x61, + 0x17, 0x2b, 0x04, 0x7e, 0xba, 0x77, 0xd6, 0x26, 0xe1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0c, 0x7d }; + +// The round constant word array, Rcon[i], contains the values given by x to the +// power (i-1) being powers of x (x is denoted as {02}) in the field GF(2^8) +static const uint8_t Rcon[11] = { + 0x8d, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36 }; + +/* + * Jordan Goulder points out in PR #12 + * (https://github.com/kokke/tiny-AES-C/pull/12), that you can remove most of + * the elements in the Rcon array, because they are unused. + * + * From Wikipedia's article on the Rijndael key schedule @ + * https://en.wikipedia.org/wiki/Rijndael_key_schedule#Rcon + * + * "Only the first some of these constants are actually used – up to rcon[10] + * for AES-128 (as 11 round keys are needed), up to rcon[8] for AES-192, up to + * rcon[7] for AES-256. rcon[0] is not used in AES algorithm." + */ + + +/*****************************************************************************/ +/* Private functions: */ +/*****************************************************************************/ +static const uint8_t *GetRoundKey(const struct AES_ctx *ctx) { + switch (ctx->KeyLength) { +#if defined(AES128) && (AES128 == 1) + case 16: return ctx->RoundKey128; +#endif +#if defined(AES192) && (AES192 == 1) + case 24: return ctx->RoundKey192; +#endif +#if defined(AES256) && (AES256 == 1) + case 32: return ctx->RoundKey256; +#endif + } + return NULL; +} + + +/* +static uint8_t getSBoxValue(uint8_t num) +{ + return sbox[num]; +} +*/ +#define getSBoxValue(num) (sbox[(num)]) +/* +static uint8_t getSBoxInvert(uint8_t num) +{ + return rsbox[num]; +} +*/ +#define getSBoxInvert(num) (rsbox[(num)]) + +// This function produces Nb(Nr+1) round keys. The round keys are used in each +// round to decrypt the states. +static void KeyExpansion(struct AES_ctx* ctx, const uint8_t* Key) +{ + uint8_t* RoundKey = (uint8_t *)GetRoundKey(ctx); + + unsigned i, j, k; + uint8_t tempa[4]; // Used for the column/row operations + + // The first round key is the key itself. + for (i = 0; i < ctx->Nk; ++i) + { + RoundKey[(i * 4) + 0] = Key[(i * 4) + 0]; + RoundKey[(i * 4) + 1] = Key[(i * 4) + 1]; + RoundKey[(i * 4) + 2] = Key[(i * 4) + 2]; + RoundKey[(i * 4) + 3] = Key[(i * 4) + 3]; + } + + // All other round keys are found from the previous round keys. + for (i = ctx->Nk; i < Nb * (ctx->Nr + 1); ++i) + { + { + k = (i - 1) * 4; + tempa[0]=RoundKey[k + 0]; + tempa[1]=RoundKey[k + 1]; + tempa[2]=RoundKey[k + 2]; + tempa[3]=RoundKey[k + 3]; + + } + + if (i % ctx->Nk == 0) + { + // This function shifts the 4 bytes in a word to the left once. + // [a0,a1,a2,a3] becomes [a1,a2,a3,a0] + + // Function RotWord() + { + const uint8_t u8tmp = tempa[0]; + tempa[0] = tempa[1]; + tempa[1] = tempa[2]; + tempa[2] = tempa[3]; + tempa[3] = u8tmp; + } + + // SubWord() is a function that takes a four-byte input word and applies + // the S-box to each of the four bytes to produce an output word. + + // Function Subword() + { + tempa[0] = getSBoxValue(tempa[0]); + tempa[1] = getSBoxValue(tempa[1]); + tempa[2] = getSBoxValue(tempa[2]); + tempa[3] = getSBoxValue(tempa[3]); + } + + tempa[0] = tempa[0] ^ Rcon[i/ctx->Nk]; + } +#if defined(AES256) && (AES256 == 1) + if (ctx->KeyLength == 32) { + if (i % ctx->Nk == 4) + { + // Function Subword() + { + tempa[0] = getSBoxValue(tempa[0]); + tempa[1] = getSBoxValue(tempa[1]); + tempa[2] = getSBoxValue(tempa[2]); + tempa[3] = getSBoxValue(tempa[3]); + } + } + } +#endif + j = i * 4; k=(i - ctx->Nk) * 4; + RoundKey[j + 0] = RoundKey[k + 0] ^ tempa[0]; + RoundKey[j + 1] = RoundKey[k + 1] ^ tempa[1]; + RoundKey[j + 2] = RoundKey[k + 2] ^ tempa[2]; + RoundKey[j + 3] = RoundKey[k + 3] ^ tempa[3]; + } +} + +void AES_init_ctx(struct AES_ctx* ctx, const uint8_t* key, uint32_t keylen) +{ + ctx->KeyLength = keylen; + switch (ctx->KeyLength) { +#if defined(AES128) && (AES128 == 1) + case 16: ctx->Nr = Nr128; ctx->Nk = Nk128; break; +#endif +#if defined(AES192) && (AES192 == 1) + case 24: ctx->Nr = Nr192; ctx->Nk = Nk192; break; +#endif +#if defined(AES256) && (AES256 == 1) + case 32: ctx->Nr = Nr256; ctx->Nk = Nk256; break; +#endif + default: ctx->Nr = 0; ctx->Nk = 0; break; + } + KeyExpansion(ctx, key); +} +#if (defined(CBC) && (CBC == 1)) || (defined(CTR) && (CTR == 1)) +void AES_init_ctx_iv(struct AES_ctx* ctx, const uint8_t* key, uint32_t keylen, const uint8_t* iv) +{ + AES_init_ctx(ctx, key, keylen); + memcpy (ctx->Iv, iv, AES_BLOCKLEN); +} +void AES_ctx_set_iv(struct AES_ctx* ctx, const uint8_t* iv) +{ + memcpy (ctx->Iv, iv, AES_BLOCKLEN); +} +#endif + +// This function adds the round key to state. The round key is added to the +// state by an XOR function. +static void AddRoundKey(uint8_t round, state_t* state, const uint8_t* RoundKey) +{ + uint8_t i,j; + for (i = 0; i < 4; ++i) + { + for (j = 0; j < 4; ++j) + { + (*state)[i][j] ^= RoundKey[(round * Nb * 4) + (i * Nb) + j]; + } + } +} + +// The SubBytes Function Substitutes the values in the state matrix with values +// in an S-box. +static void SubBytes(state_t* state) +{ + uint8_t i, j; + for (i = 0; i < 4; ++i) + { + for (j = 0; j < 4; ++j) + { + (*state)[j][i] = getSBoxValue((*state)[j][i]); + } + } +} + +// The ShiftRows() function shifts the rows in the state to the left. Each row +// is shifted with different offset. Offset = Row number. So the first row is +// not shifted. +static void ShiftRows(state_t* state) +{ + uint8_t temp; + + // Rotate first row 1 columns to left + temp = (*state)[0][1]; + (*state)[0][1] = (*state)[1][1]; + (*state)[1][1] = (*state)[2][1]; + (*state)[2][1] = (*state)[3][1]; + (*state)[3][1] = temp; + + // Rotate second row 2 columns to left + temp = (*state)[0][2]; + (*state)[0][2] = (*state)[2][2]; + (*state)[2][2] = temp; + + temp = (*state)[1][2]; + (*state)[1][2] = (*state)[3][2]; + (*state)[3][2] = temp; + + // Rotate third row 3 columns to left + temp = (*state)[0][3]; + (*state)[0][3] = (*state)[3][3]; + (*state)[3][3] = (*state)[2][3]; + (*state)[2][3] = (*state)[1][3]; + (*state)[1][3] = temp; +} + +static uint8_t xtime(uint8_t x) +{ + return ((x<<1) ^ (((x>>7) & 1) * 0x1b)); +} + +// MixColumns function mixes the columns of the state matrix +static void MixColumns(state_t* state) +{ + uint8_t i; + uint8_t Tmp, Tm, t; + for (i = 0; i < 4; ++i) + { + t = (*state)[i][0]; + Tmp = (*state)[i][0] ^ (*state)[i][1] ^ (*state)[i][2] ^ (*state)[i][3] ; + Tm = (*state)[i][0] ^ (*state)[i][1] ; Tm = xtime(Tm); (*state)[i][0] ^= Tm ^ Tmp ; + Tm = (*state)[i][1] ^ (*state)[i][2] ; Tm = xtime(Tm); (*state)[i][1] ^= Tm ^ Tmp ; + Tm = (*state)[i][2] ^ (*state)[i][3] ; Tm = xtime(Tm); (*state)[i][2] ^= Tm ^ Tmp ; + Tm = (*state)[i][3] ^ t ; Tm = xtime(Tm); (*state)[i][3] ^= Tm ^ Tmp ; + } +} + +// Multiply is used to multiply numbers in the field GF(2^8) +// Note: The last call to xtime() is unneeded, but often ends up generating a smaller binary +// The compiler seems to be able to vectorize the operation better this way. +// See https://github.com/kokke/tiny-AES-c/pull/34 +#if MULTIPLY_AS_A_FUNCTION +static uint8_t Multiply(uint8_t x, uint8_t y) +{ + return (((y & 1) * x) ^ + ((y>>1 & 1) * xtime(x)) ^ + ((y>>2 & 1) * xtime(xtime(x))) ^ + ((y>>3 & 1) * xtime(xtime(xtime(x)))) ^ + ((y>>4 & 1) * xtime(xtime(xtime(xtime(x)))))); /* this last call to xtime() can be omitted */ + } +#else +#define Multiply(x, y) \ + ( ((y & 1) * x) ^ \ + ((y>>1 & 1) * xtime(x)) ^ \ + ((y>>2 & 1) * xtime(xtime(x))) ^ \ + ((y>>3 & 1) * xtime(xtime(xtime(x)))) ^ \ + ((y>>4 & 1) * xtime(xtime(xtime(xtime(x)))))) \ + +#endif + +#if (defined(CBC) && CBC == 1) || (defined(ECB) && ECB == 1) +// MixColumns function mixes the columns of the state matrix. The method used to +// multiply may be difficult to understand for the inexperienced. Please use the +// references to gain more information. +static void InvMixColumns(state_t* state) +{ + int i; + uint8_t a, b, c, d; + for (i = 0; i < 4; ++i) + { + a = (*state)[i][0]; + b = (*state)[i][1]; + c = (*state)[i][2]; + d = (*state)[i][3]; + + (*state)[i][0] = Multiply(a, 0x0e) ^ Multiply(b, 0x0b) ^ Multiply(c, 0x0d) ^ Multiply(d, 0x09); + (*state)[i][1] = Multiply(a, 0x09) ^ Multiply(b, 0x0e) ^ Multiply(c, 0x0b) ^ Multiply(d, 0x0d); + (*state)[i][2] = Multiply(a, 0x0d) ^ Multiply(b, 0x09) ^ Multiply(c, 0x0e) ^ Multiply(d, 0x0b); + (*state)[i][3] = Multiply(a, 0x0b) ^ Multiply(b, 0x0d) ^ Multiply(c, 0x09) ^ Multiply(d, 0x0e); + } +} + + +// The SubBytes Function Substitutes the values in the state matrix with values +// in an S-box. +static void InvSubBytes(state_t* state) +{ + uint8_t i, j; + for (i = 0; i < 4; ++i) + { + for (j = 0; j < 4; ++j) + { + (*state)[j][i] = getSBoxInvert((*state)[j][i]); + } + } +} + +static void InvShiftRows(state_t* state) +{ + uint8_t temp; + + // Rotate first row 1 columns to right + temp = (*state)[3][1]; + (*state)[3][1] = (*state)[2][1]; + (*state)[2][1] = (*state)[1][1]; + (*state)[1][1] = (*state)[0][1]; + (*state)[0][1] = temp; + + // Rotate second row 2 columns to right + temp = (*state)[0][2]; + (*state)[0][2] = (*state)[2][2]; + (*state)[2][2] = temp; + + temp = (*state)[1][2]; + (*state)[1][2] = (*state)[3][2]; + (*state)[3][2] = temp; + + // Rotate third row 3 columns to right + temp = (*state)[0][3]; + (*state)[0][3] = (*state)[1][3]; + (*state)[1][3] = (*state)[2][3]; + (*state)[2][3] = (*state)[3][3]; + (*state)[3][3] = temp; +} +#endif // #if (defined(CBC) && CBC == 1) || (defined(ECB) && ECB == 1) + +// Cipher is the main function that encrypts the PlainText. +static void Cipher(state_t* state, const struct AES_ctx* ctx) +{ + const uint8_t* RoundKey = GetRoundKey(ctx); + uint8_t round = 0; + + // Add the First round key to the state before starting the rounds. + AddRoundKey(0, state, RoundKey); + + // There will be Nr rounds. The first Nr-1 rounds are identical. These Nr + // rounds are executed in the loop below. Last one without MixColumns() + for (round = 1; ; ++round) + { + SubBytes(state); + ShiftRows(state); + if (round == ctx->Nr) { + break; + } + MixColumns(state); + AddRoundKey(round, state, RoundKey); + } + // Add round key to last round + AddRoundKey(ctx->Nr, state, RoundKey); +} + +#if (defined(CBC) && CBC == 1) || (defined(ECB) && ECB == 1) +static void InvCipher(state_t* state, const struct AES_ctx* ctx) +{ + const uint8_t* RoundKey = GetRoundKey(ctx); + uint8_t round = 0; + + // Add the First round key to the state before starting the rounds. + AddRoundKey(ctx->Nr, state, RoundKey); + + // There will be Nr rounds. The first Nr-1 rounds are identical. These Nr + // rounds are executed in the loop below. Last one without InvMixColumn() + for (round = (ctx->Nr - 1); ; --round) + { + InvShiftRows(state); + InvSubBytes(state); + AddRoundKey(round, state, RoundKey); + if (round == 0) { + break; + } + InvMixColumns(state); + } + +} +#endif // #if (defined(CBC) && CBC == 1) || (defined(ECB) && ECB == 1) + +/*****************************************************************************/ +/* Public functions: */ +/*****************************************************************************/ +#if defined(ECB) && (ECB == 1) + + +void AES_ECB_encrypt(const struct AES_ctx* ctx, uint8_t* buf) +{ + // The next function call encrypts the PlainText with the Key using AES + // algorithm. + Cipher((state_t*)buf, ctx); +} + +void AES_ECB_decrypt(const struct AES_ctx* ctx, uint8_t* buf) +{ + // The next function call decrypts the PlainText with the Key using AES + // algorithm. + InvCipher((state_t*)buf, ctx); +} + + +#endif // #if defined(ECB) && (ECB == 1) + + + + + +#if defined(CBC) && (CBC == 1) + + +static void XorWithIv(uint8_t* buf, const uint8_t* Iv) +{ + uint8_t i; + for (i = 0; i < AES_BLOCKLEN; ++i) // The block in AES is always 128bit no matter the key size + { + buf[i] ^= Iv[i]; + } +} + +void AES_CBC_encrypt_buffer(struct AES_ctx *ctx, uint8_t* buf, uint32_t length) +{ + uintptr_t i; + uint8_t *Iv = ctx->Iv; + for (i = 0; i < length; i += AES_BLOCKLEN) + { + XorWithIv(buf, Iv); + Cipher((state_t*)buf, ctx); + Iv = buf; + buf += AES_BLOCKLEN; + } + /* store Iv in ctx for next call */ + memcpy(ctx->Iv, Iv, AES_BLOCKLEN); +} + +void AES_CBC_decrypt_buffer(struct AES_ctx* ctx, uint8_t* buf, uint32_t length) +{ + uintptr_t i; + uint8_t storeNextIv[AES_BLOCKLEN]; + for (i = 0; i < length; i += AES_BLOCKLEN) + { + memcpy(storeNextIv, buf, AES_BLOCKLEN); + InvCipher((state_t*)buf, ctx); + XorWithIv(buf, ctx->Iv); + memcpy(ctx->Iv, storeNextIv, AES_BLOCKLEN); + buf += AES_BLOCKLEN; + } + +} + +#endif // #if defined(CBC) && (CBC == 1) + + + +#if defined(CTR) && (CTR == 1) + +/* Symmetrical operation: same function for encrypting as for decrypting. Note +any IV/nonce should never be reused with the same key */ +void AES_CTR_xcrypt_buffer(struct AES_ctx* ctx, uint8_t* buf, uint32_t length) +{ + uint8_t buffer[AES_BLOCKLEN]; + + unsigned i; + int bi; + for (i = 0, bi = AES_BLOCKLEN; i < length; ++i, ++bi) + { + if (bi == AES_BLOCKLEN) /* we need to regen xor compliment in buffer */ + { + memcpy(buffer, ctx->Iv, AES_BLOCKLEN); + Cipher((state_t*)buffer, ctx); + + /* Increment Iv and handle overflow */ + for (bi = (AES_BLOCKLEN - 1); bi >= 0; --bi) + { + /* inc will overflow */ + if (ctx->Iv[bi] == 255) + { + ctx->Iv[bi] = 0; + continue; + } + ctx->Iv[bi] += 1; + break; + } + bi = 0; + } + + buf[i] = (buf[i] ^ buffer[bi]); + } +} + +#endif // #if defined(CTR) && (CTR == 1) diff --git a/shared-module/aesio/aes.h b/shared-module/aesio/aes.h new file mode 100644 index 000000000..a87fa5be7 --- /dev/null +++ b/shared-module/aesio/aes.h @@ -0,0 +1,105 @@ +#ifndef _AES_H_ +#define _AES_H_ + +#include <stdint.h> + +// #define the macros below to 1/0 to enable/disable the mode of operation. +// +// CBC enables AES encryption in CBC-mode of operation. +// CTR enables encryption in counter-mode. +// ECB enables the basic ECB 16-byte block algorithm. All can be enabled simultaneously. + +// The #ifndef-guard allows it to be configured before #include'ing or at compile time. +#ifndef CBC + #define CBC 1 +#endif + +#ifndef ECB + #define ECB 1 +#endif + +#ifndef CTR + #define CTR 1 +#endif + + +#define AES128 1 +#define AES192 1 +#define AES256 1 + +#define AES_BLOCKLEN 16 // Block length in bytes - AES is 128b block only + +#if defined(AES256) && (AES256 == 1) + #define AES_KEYLEN256 32 + #define AES_keyExpSize256 240 +#endif +#if defined(AES192) && (AES192 == 1) + #define AES_KEYLEN192 24 + #define AES_keyExpSize192 208 +#endif +#if defined(AES128) && (AES128 == 1) + #define AES_KEYLEN128 16 // Key length in bytes + #define AES_keyExpSize128 176 +#endif + +struct AES_ctx +{ + union { +#if defined(AES256) && (AES256 == 1) + uint8_t RoundKey256[AES_keyExpSize256]; +#endif +#if defined(AES192) && (AES192 == 1) + uint8_t RoundKey192[AES_keyExpSize192]; +#endif +#if defined(AES128) && (AES128 == 1) + uint8_t RoundKey128[AES_keyExpSize128]; +#endif + }; +#if (defined(CBC) && (CBC == 1)) || (defined(CTR) && (CTR == 1)) + uint8_t Iv[AES_BLOCKLEN]; +#endif + uint32_t KeyLength; + uint8_t Nr; + uint8_t Nk; +}; + +void AES_init_ctx(struct AES_ctx* ctx, const uint8_t* key, uint32_t keylen); +#if (defined(CBC) && (CBC == 1)) || (defined(CTR) && (CTR == 1)) +void AES_init_ctx_iv(struct AES_ctx* ctx, const uint8_t* key, uint32_t keylen, const uint8_t* iv); +void AES_ctx_set_iv(struct AES_ctx* ctx, const uint8_t* iv); +#endif + +#if defined(ECB) && (ECB == 1) +// buffer size is exactly AES_BLOCKLEN bytes; +// you need only AES_init_ctx as IV is not used in ECB +// NB: ECB is considered insecure for most uses +void AES_ECB_encrypt(const struct AES_ctx* ctx, uint8_t* buf); +void AES_ECB_decrypt(const struct AES_ctx* ctx, uint8_t* buf); + +#endif // #if defined(ECB) && (ECB == !) + + +#if defined(CBC) && (CBC == 1) +// buffer size MUST be mutile of AES_BLOCKLEN; +// Suggest https://en.wikipedia.org/wiki/Padding_(cryptography)#PKCS7 for padding scheme +// NOTES: you need to set IV in ctx via AES_init_ctx_iv() or AES_ctx_set_iv() +// no IV should ever be reused with the same key +void AES_CBC_encrypt_buffer(struct AES_ctx* ctx, uint8_t* buf, uint32_t length); +void AES_CBC_decrypt_buffer(struct AES_ctx* ctx, uint8_t* buf, uint32_t length); + +#endif // #if defined(CBC) && (CBC == 1) + + +#if defined(CTR) && (CTR == 1) + +// Same function for encrypting as for decrypting. +// IV is incremented for every block, and used after encryption as XOR-compliment for output +// Suggesting https://en.wikipedia.org/wiki/Padding_(cryptography)#PKCS7 for padding scheme +// NOTES: you need to set IV in ctx with AES_init_ctx_iv() or AES_ctx_set_iv() +// no IV should ever be reused with the same key +void AES_CTR_xcrypt_buffer(struct AES_ctx* ctx, uint8_t* buf, uint32_t length); + +#endif // #if defined(CTR) && (CTR == 1) + + +#endif // _AES_H_ diff --git a/shared-module/audiomixer/Mixer.c b/shared-module/audiomixer/Mixer.c index 4a72dab28..6c1ba1973 100644 --- a/shared-module/audiomixer/Mixer.c +++ b/shared-module/audiomixer/Mixer.c @@ -143,19 +143,17 @@ static inline uint32_t pack8(uint32_t val) { static void mix_down_one_voice(audiomixer_mixer_obj_t* self, audiomixer_mixervoice_obj_t* voice, bool voices_active, uint32_t* word_buffer, uint32_t length) { - bool voice_done = voice->sample == NULL; - while (!voice_done && length != 0) { + while (length != 0) { if (voice->buffer_length == 0) { if (!voice->more_data) { if (voice->loop) { audiosample_reset_buffer(voice->sample, false, 0); } else { voice->sample = NULL; - voice_done = true; break; } } - if (!voice_done) { + if (voice->sample) { // Load another buffer audioio_get_buffer_result_t result = audiosample_get_buffer(voice->sample, false, 0, (uint8_t**) &voice->remaining_buffer, &voice->buffer_length); // Track length in terms of words. @@ -230,10 +228,8 @@ static void mix_down_one_voice(audiomixer_mixer_obj_t* self, } if (length && !voices_active) { - uint32_t sample_value = self->bits_per_sample == 8 - ? 0x80808080 : 0x80008000; for (uint32_t i = 0; i<length; i++) { - word_buffer[i] = sample_value; + word_buffer[i] = 0; } } } @@ -269,9 +265,16 @@ audioio_get_buffer_result_t audiomixer_mixer_get_buffer(audiomixer_mixer_obj_t* for (int32_t v = 0; v < self->voice_count; v++) { audiomixer_mixervoice_obj_t* voice = MP_OBJ_TO_PTR(self->voice[v]); + if(voice->sample) { + mix_down_one_voice(self, voice, voices_active, word_buffer, length); + voices_active = true; + } + } - mix_down_one_voice(self, voice, voices_active, word_buffer, length); - voices_active = true; + if (!voices_active) { + for (uint32_t i = 0; i<length; i++) { + word_buffer[i] = 0; + } } if (!self->samples_signed) { diff --git a/shared-module/audiomp3/MP3Decoder.c b/shared-module/audiomp3/MP3Decoder.c index 30357c616..5f3f42a51 100644 --- a/shared-module/audiomp3/MP3Decoder.c +++ b/shared-module/audiomp3/MP3Decoder.c @@ -36,11 +36,12 @@ #include "shared-module/audiomp3/MP3Decoder.h" #include "supervisor/shared/translate.h" +#include "supervisor/background_callback.h" #include "lib/mp3/src/mp3common.h" #define MAX_BUFFER_LEN (MAX_NSAMP * MAX_NGRAN * MAX_NCHAN * sizeof(int16_t)) -/** Fill the input buffer if it is less than half full. +/** Fill the input buffer unconditionally. * * Returns true if the input buffer contains any useful data, * false otherwise. (The input buffer will be padded to the end with @@ -50,10 +51,7 @@ * * Sets self->eof if any read of the file returns 0 bytes */ -STATIC bool mp3file_update_inbuf(audiomp3_mp3file_obj_t* self) { - // If buffer is over half full, do nothing - if (self->inbuf_offset < self->inbuf_length/2) return true; - +STATIC bool mp3file_update_inbuf_always(audiomp3_mp3file_obj_t* self) { // If we didn't previously reach the end of file, we can try reading now if (!self->eof) { @@ -87,6 +85,26 @@ STATIC bool mp3file_update_inbuf(audiomp3_mp3file_obj_t* self) { return self->inbuf_offset < self->inbuf_length; } +/** Update the inbuf from a background callback. + * + * This variant is introduced so that at the site of the + * add_background_callback_core call, the prototype matches. + */ +STATIC void mp3file_update_inbuf_cb(void* self) { + mp3file_update_inbuf_always(self); +} + +/** Fill the input buffer if it is less than half full. + * + * Returns the same as mp3file_update_inbuf_always. + */ +STATIC bool mp3file_update_inbuf_half(audiomp3_mp3file_obj_t* self) { + // If buffer is over half full, do nothing + if (self->inbuf_offset < self->inbuf_length/2) return true; + + return mp3file_update_inbuf_always(self); +} + #define READ_PTR(self) (self->inbuf + self->inbuf_offset) #define BYTES_LEFT(self) (self->inbuf_length - self->inbuf_offset) #define CONSUME(self, n) (self->inbuf_offset += n) @@ -94,7 +112,7 @@ STATIC bool mp3file_update_inbuf(audiomp3_mp3file_obj_t* self) { // http://id3.org/d3v2.3.0 // http://id3.org/id3v2.3.0 STATIC void mp3file_skip_id3v2(audiomp3_mp3file_obj_t* self) { - mp3file_update_inbuf(self); + mp3file_update_inbuf_half(self); if (BYTES_LEFT(self) < 10) { return; } @@ -129,11 +147,11 @@ STATIC void mp3file_skip_id3v2(audiomp3_mp3file_obj_t* self) { */ STATIC bool mp3file_find_sync_word(audiomp3_mp3file_obj_t* self) { do { - mp3file_update_inbuf(self); + mp3file_update_inbuf_half(self); int offset = MP3FindSyncWord(READ_PTR(self), BYTES_LEFT(self)); if (offset >= 0) { CONSUME(self, offset); - mp3file_update_inbuf(self); + mp3file_update_inbuf_half(self); return true; } CONSUME(self, MAX(0, BYTES_LEFT(self) - 16)); @@ -209,12 +227,14 @@ void common_hal_audiomp3_mp3file_construct(audiomp3_mp3file_obj_t* self, } void common_hal_audiomp3_mp3file_set_file(audiomp3_mp3file_obj_t* self, pyb_file_obj_t* file) { + background_callback_begin_critical_section(); + self->file = file; f_lseek(&self->file->fp, 0); self->inbuf_offset = self->inbuf_length; self->eof = 0; self->other_channel = -1; - mp3file_update_inbuf(self); + mp3file_update_inbuf_half(self); mp3file_find_sync_word(self); // It **SHOULD** not be necessary to do this; the buffer should be filled // with fresh content before it is returned by get_buffer(). The fact that @@ -224,7 +244,9 @@ void common_hal_audiomp3_mp3file_set_file(audiomp3_mp3file_obj_t* self, pyb_file memset(self->buffers[0], 0, MAX_BUFFER_LEN); memset(self->buffers[1], 0, MAX_BUFFER_LEN); MP3FrameInfo fi; - if(!mp3file_get_next_frame_info(self, &fi)) { + bool result = mp3file_get_next_frame_info(self, &fi); + background_callback_end_critical_section(); + if (!result) { mp_raise_msg(&mp_type_RuntimeError, translate("Failed to parse MP3 file")); } @@ -277,13 +299,15 @@ void audiomp3_mp3file_reset_buffer(audiomp3_mp3file_obj_t* self, } // We don't reset the buffer index in case we're looping and we have an odd number of buffer // loads + background_callback_begin_critical_section(); f_lseek(&self->file->fp, 0); self->inbuf_offset = self->inbuf_length; self->eof = 0; self->other_channel = -1; - mp3file_update_inbuf(self); + mp3file_update_inbuf_half(self); mp3file_skip_id3v2(self); mp3file_find_sync_word(self); + background_callback_end_critical_section(); } audioio_get_buffer_result_t audiomp3_mp3file_get_buffer(audiomp3_mp3file_obj_t* self, @@ -321,6 +345,14 @@ audioio_get_buffer_result_t audiomp3_mp3file_get_buffer(audiomp3_mp3file_obj_t* uint8_t *inbuf = READ_PTR(self); int err = MP3Decode(self->decoder, &inbuf, &bytes_left, buffer, 0); CONSUME(self, BYTES_LEFT(self) - bytes_left); + + if (self->inbuf_offset >= 512) { + background_callback_add( + &self->inbuf_fill_cb, + mp3file_update_inbuf_cb, + self); + } + if (err) { return GET_BUFFER_DONE; } diff --git a/shared-module/audiomp3/MP3Decoder.h b/shared-module/audiomp3/MP3Decoder.h index 9ee1d0949..f91f102a2 100644 --- a/shared-module/audiomp3/MP3Decoder.h +++ b/shared-module/audiomp3/MP3Decoder.h @@ -28,6 +28,7 @@ #ifndef MICROPY_INCLUDED_SHARED_MODULE_AUDIOIO_MP3FILE_H #define MICROPY_INCLUDED_SHARED_MODULE_AUDIOIO_MP3FILE_H +#include "supervisor/background_callback.h" #include "extmod/vfs_fat.h" #include "py/obj.h" @@ -36,6 +37,7 @@ typedef struct { mp_obj_base_t base; struct _MP3DecInfo *decoder; + background_callback_t inbuf_fill_cb; uint8_t* inbuf; uint32_t inbuf_length; uint32_t inbuf_offset; diff --git a/shared-module/bitbangio/I2C.c b/shared-module/bitbangio/I2C.c index 5a9ac9a99..d44aec0e7 100644 --- a/shared-module/bitbangio/I2C.c +++ b/shared-module/bitbangio/I2C.c @@ -3,7 +3,7 @@ * * The MIT License (MIT) * - * Copyright (c) 2016 Damien P. George, Scott Shawcroft + * SPDX-FileCopyrightText: Copyright (c) 2016 Damien P. George, Scott Shawcroft * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal diff --git a/shared-module/bitbangio/SPI.c b/shared-module/bitbangio/SPI.c index e0ff1184f..f3fe16029 100644 --- a/shared-module/bitbangio/SPI.c +++ b/shared-module/bitbangio/SPI.c @@ -3,7 +3,7 @@ * * The MIT License (MIT) * - * Copyright (c) 2013, 2014 Damien P. George + * SPDX-FileCopyrightText: Copyright (c) 2013, 2014 Damien P. George * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal diff --git a/shared-module/board/__init__.c b/shared-module/board/__init__.c index 789318a05..2f1c34e56 100644 --- a/shared-module/board/__init__.c +++ b/shared-module/board/__init__.c @@ -39,6 +39,11 @@ #include "shared-module/displayio/__init__.h" #endif +#if CIRCUITPY_SHARPDISPLAY +#include "shared-bindings/sharpdisplay/SharpMemoryFramebuffer.h" +#include "shared-module/sharpdisplay/SharpMemoryFramebuffer.h" +#endif + #if BOARD_I2C // Statically allocate the I2C object so it can live past the end of the heap and into the next VM. // That way it can be used by built-in I2CDisplay displays and be accessible through board.I2C(). @@ -50,13 +55,12 @@ mp_obj_t common_hal_board_get_i2c(void) { } mp_obj_t common_hal_board_create_i2c(void) { - if (i2c_singleton != NULL) { - return i2c_singleton; - } + // All callers have either already verified this or come so early that it can't be otherwise. + assert(i2c_singleton == NULL || common_hal_busio_i2c_deinited(i2c_singleton)); busio_i2c_obj_t *self = &i2c_obj; self->base.type = &busio_i2c_type; - common_hal_busio_i2c_construct(self, DEFAULT_I2C_BUS_SCL, DEFAULT_I2C_BUS_SDA, 400000, 0); + common_hal_busio_i2c_construct(self, DEFAULT_I2C_BUS_SCL, DEFAULT_I2C_BUS_SDA, 100000, 0); i2c_singleton = (mp_obj_t)self; return i2c_singleton; } @@ -74,9 +78,8 @@ mp_obj_t common_hal_board_get_spi(void) { } mp_obj_t common_hal_board_create_spi(void) { - if (spi_singleton != NULL) { - return spi_singleton; - } + // All callers have either already verified this or come so early that it can't be otherwise. + assert(spi_singleton == NULL || common_hal_busio_spi_deinited(spi_singleton)); busio_spi_obj_t *self = &spi_obj; self->base.type = &busio_spi_type; @@ -123,7 +126,7 @@ mp_obj_t common_hal_board_create_uart(void) { #endif common_hal_busio_uart_construct(self, tx, rx, rts, cts, rs485_dir, rs485_invert, - 9600, 8, PARITY_NONE, 1, 1.0f, 64); + 9600, 8, BUSIO_UART_PARITY_NONE, 1, 1.0f, 64, NULL, false); MP_STATE_VM(shared_uart_bus) = MP_OBJ_FROM_PTR(self); return MP_STATE_VM(shared_uart_bus); } @@ -134,28 +137,43 @@ void reset_board_busses(void) { bool display_using_i2c = false; #if CIRCUITPY_DISPLAYIO for (uint8_t i = 0; i < CIRCUITPY_DISPLAY_LIMIT; i++) { - if (displays[i].i2cdisplay_bus.bus == i2c_singleton) { + if (displays[i].bus_base.type == &displayio_i2cdisplay_type && displays[i].i2cdisplay_bus.bus == i2c_singleton) { display_using_i2c = true; break; } } #endif - if (!display_using_i2c) { - i2c_singleton = NULL; + if (i2c_singleton != NULL) { + if (!display_using_i2c) { + common_hal_busio_i2c_deinit(i2c_singleton); + i2c_singleton = NULL; + } } #endif #if BOARD_SPI bool display_using_spi = false; #if CIRCUITPY_DISPLAYIO for (uint8_t i = 0; i < CIRCUITPY_DISPLAY_LIMIT; i++) { - if (displays[i].fourwire_bus.bus == spi_singleton) { + mp_const_obj_t bus_type = displays[i].bus_base.type; + if (bus_type == &displayio_fourwire_type && displays[i].fourwire_bus.bus == spi_singleton) { + display_using_spi = true; + break; + } + #if CIRCUITPY_SHARPDISPLAY + if (displays[i].bus_base.type == &sharpdisplay_framebuffer_type && displays[i].sharpdisplay.bus == spi_singleton) { display_using_spi = true; break; } + #endif } #endif - if (!display_using_spi) { - spi_singleton = NULL; + // make sure SPI lock is not held over a soft reset + if (spi_singleton != NULL) { + common_hal_busio_spi_unlock(spi_singleton); + if (!display_using_spi) { + common_hal_busio_spi_deinit(spi_singleton); + spi_singleton = NULL; + } } #endif #if BOARD_UART diff --git a/shared-module/canio/Match.c b/shared-module/canio/Match.c new file mode 100644 index 000000000..b4e8616e9 --- /dev/null +++ b/shared-module/canio/Match.c @@ -0,0 +1,43 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2020 Jeff Epler for Adafruit Industries + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "shared-module/canio/Match.h" + +void common_hal_canio_match_construct(canio_match_obj_t *self, int id, int mask, bool extended) { + self->id = id; + self->mask = mask; + self->extended = extended; +} + +int common_hal_canio_match_get_id(const canio_match_obj_t *self) { + return self->id; +} +int common_hal_canio_match_get_mask(const canio_match_obj_t *self) { + return self->mask; +} +bool common_hal_canio_match_get_extended(const canio_match_obj_t *self) { + return self->extended; +} diff --git a/shared-module/_pew/__init__.h b/shared-module/canio/Match.h index 4f953c610..b52191d7c 100644 --- a/shared-module/_pew/__init__.h +++ b/shared-module/canio/Match.h @@ -3,7 +3,7 @@ * * The MIT License (MIT) * - * Copyright (c) 2019 Radomir Dopieralski + * Copyright (c) 2020 Jeff Epler for Adafruit Industries * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -24,9 +24,13 @@ * THE SOFTWARE. */ -#ifndef MICROPY_INCLUDED_PEW_H -#define MICROPY_INCLUDED_PEW_H +#pragma once -void pew_tick(void); +#include "py/obj.h" -#endif // MICROPY_INCLUDED_PEW_H +typedef struct { + mp_obj_base_t base; + int id; + int mask; + bool extended; +} canio_match_obj_t; diff --git a/shared-module/canio/Message.c b/shared-module/canio/Message.c new file mode 100644 index 000000000..a1df4f693 --- /dev/null +++ b/shared-module/canio/Message.c @@ -0,0 +1,77 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2020 Jeff Epler for Adafruit Industries + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "shared-module/canio/Message.h" + +#include <string.h> + +void common_hal_canio_message_construct(canio_message_obj_t *self, int id, void *data, size_t size, bool extended) +{ + self->id = id; + self->size = size; + self->extended = extended; + if (data) { + memcpy(self->data, data, size); + } +} + +int common_hal_canio_message_get_id(const canio_message_obj_t *self) +{ + return self->id; +} + +void common_hal_canio_message_set_id(canio_message_obj_t *self, int id) +{ + self->id = id; +} + + +const void *common_hal_canio_message_get_data(const canio_message_obj_t *self) +{ + return self->data; +} + +const void common_hal_canio_message_set_data(canio_message_obj_t *self, const void *data, size_t size) +{ + self->size = size; + memcpy(self->data, data, size); +} + + +size_t common_hal_canio_message_get_length(const canio_message_obj_t *self) +{ + return self->size; +} + +bool common_hal_canio_message_get_extended(const canio_message_obj_t *self) +{ + return self->extended; +} + +void common_hal_canio_message_set_extended(canio_message_obj_t *self, bool extended) +{ + self->extended = extended; +} diff --git a/shared-module/canio/Message.h b/shared-module/canio/Message.h new file mode 100644 index 000000000..1ca0d42e2 --- /dev/null +++ b/shared-module/canio/Message.h @@ -0,0 +1,37 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2020 Jeff Epler for Adafruit Industries + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#pragma once + +#include "py/obj.h" + +typedef struct { + mp_obj_base_t base; + int id; + uint8_t data[8]; + size_t size:4; + bool extended:1; +} canio_message_obj_t; diff --git a/shared-module/canio/RemoteTransmissionRequest.c b/shared-module/canio/RemoteTransmissionRequest.c new file mode 100644 index 000000000..9b4d5632f --- /dev/null +++ b/shared-module/canio/RemoteTransmissionRequest.c @@ -0,0 +1,67 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2020 Jeff Epler for Adafruit Industries + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "shared-module/canio/RemoteTransmissionRequest.h" +#include "shared-bindings/canio/RemoteTransmissionRequest.h" + +#include <string.h> + +void common_hal_canio_remote_transmission_request_construct(canio_remote_transmission_request_obj_t *self, int id, size_t size, bool extended) +{ + self->id = id; + self->size = size; + self->extended = extended; +} + +int common_hal_canio_remote_transmission_request_get_id(const canio_remote_transmission_request_obj_t *self) +{ + return self->id; +} + +void common_hal_canio_remote_transmission_request_set_id(canio_remote_transmission_request_obj_t *self, int id) +{ + self->id = id; +} + +size_t common_hal_canio_remote_transmission_request_get_length(const canio_remote_transmission_request_obj_t *self) +{ + return self->size; +} + +void common_hal_canio_remote_transmission_request_set_length(canio_remote_transmission_request_obj_t *self, size_t size) +{ + self->size = size; +} + +bool common_hal_canio_remote_transmission_request_get_extended(const canio_remote_transmission_request_obj_t *self) +{ + return self->extended; +} + +void common_hal_canio_remote_transmission_request_set_extended(canio_remote_transmission_request_obj_t *self, bool extended) +{ + self->extended = extended; +} diff --git a/shared-module/canio/RemoteTransmissionRequest.h b/shared-module/canio/RemoteTransmissionRequest.h new file mode 100644 index 000000000..2f09b19c6 --- /dev/null +++ b/shared-module/canio/RemoteTransmissionRequest.h @@ -0,0 +1,33 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2020 Jeff Epler for Adafruit Industries + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#pragma once + +#include "py/obj.h" + +#include "shared-bindings/canio/Message.h" + +typedef canio_message_obj_t canio_remote_transmission_request_obj_t; diff --git a/shared-module/displayio/Bitmap.c b/shared-module/displayio/Bitmap.c index 8bcda6086..d1942efc7 100644 --- a/shared-module/displayio/Bitmap.c +++ b/shared-module/displayio/Bitmap.c @@ -105,6 +105,32 @@ uint32_t common_hal_displayio_bitmap_get_pixel(displayio_bitmap_t *self, int16_t return 0; } +void common_hal_displayio_bitmap_blit(displayio_bitmap_t *self, int16_t x, int16_t y, displayio_bitmap_t *source, + int16_t x1, int16_t y1, int16_t x2, int16_t y2, uint32_t skip_index, bool skip_index_none) { + // 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. + + if (self->read_only) { + mp_raise_RuntimeError(translate("Read-only object")); + } + + // 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 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); + 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); + } + } + } + } + } +} + void common_hal_displayio_bitmap_set_pixel(displayio_bitmap_t *self, int16_t x, int16_t y, uint32_t value) { if (self->read_only) { mp_raise_RuntimeError(translate("Read-only object")); diff --git a/shared-module/displayio/ColorConverter.c b/shared-module/displayio/ColorConverter.c index 2c9fb6d78..c2c3214aa 100644 --- a/shared-module/displayio/ColorConverter.c +++ b/shared-module/displayio/ColorConverter.c @@ -110,7 +110,7 @@ void common_hal_displayio_colorconverter_convert(displayio_colorconverter_t *sel displayio_input_pixel_t input_pixel; input_pixel.pixel = input_color; input_pixel.x = input_pixel.y = input_pixel.tile = input_pixel.tile_x = input_pixel.tile_y = 0; - + displayio_output_pixel_t output_pixel; output_pixel.pixel = 0; output_pixel.opaque = false; @@ -130,7 +130,7 @@ bool common_hal_displayio_colorconverter_get_dither(displayio_colorconverter_t* void displayio_colorconverter_convert(displayio_colorconverter_t *self, const _displayio_colorspace_t* colorspace, const displayio_input_pixel_t *input_pixel, displayio_output_pixel_t *output_color) { uint32_t pixel = input_pixel->pixel; - + if (self->dither){ uint8_t randr = (displayio_colorconverter_dither_noise_2(input_pixel->tile_x,input_pixel->tile_y)); uint8_t randg = (displayio_colorconverter_dither_noise_2(input_pixel->tile_x+33,input_pixel->tile_y)); @@ -193,4 +193,3 @@ bool displayio_colorconverter_needs_refresh(displayio_colorconverter_t *self) { void displayio_colorconverter_finish_refresh(displayio_colorconverter_t *self) { } - diff --git a/shared-module/displayio/Display.c b/shared-module/displayio/Display.c index aaddbb471..7c8c9280b 100644 --- a/shared-module/displayio/Display.c +++ b/shared-module/displayio/Display.c @@ -41,8 +41,6 @@ #include <stdint.h> #include <string.h> -#include "tick.h" - void common_hal_displayio_display_construct(displayio_display_obj_t* self, mp_obj_t bus, uint16_t width, uint16_t height, int16_t colstart, int16_t rowstart, uint16_t rotation, uint16_t color_depth, bool grayscale, bool pixels_in_byte_share_row, @@ -50,7 +48,9 @@ void common_hal_displayio_display_construct(displayio_display_obj_t* self, uint8_t set_row_command, uint8_t write_ram_command, uint8_t set_vertical_scroll, uint8_t* init_sequence, uint16_t init_sequence_len, const mcu_pin_obj_t* backlight_pin, uint16_t brightness_command, mp_float_t brightness, bool auto_brightness, - bool single_byte_bounds, bool data_as_commands, bool auto_refresh, uint16_t native_frames_per_second, bool backlight_on_high) { + bool single_byte_bounds, bool data_as_commands, bool auto_refresh, uint16_t native_frames_per_second, + bool backlight_on_high, bool SH1107_addressing) { + // Turn off auto-refresh as we init. self->auto_refresh = false; uint16_t ram_width = 0x100; @@ -70,6 +70,7 @@ void common_hal_displayio_display_construct(displayio_display_obj_t* self, self->first_manual_refresh = !auto_refresh; self->data_as_commands = data_as_commands; self->backlight_on_high = backlight_on_high; + self->SH1107_addressing = SH1107_addressing; self->native_frames_per_second = native_frames_per_second; self->native_ms_per_frame = 1000 / native_frames_per_second; @@ -106,20 +107,26 @@ void common_hal_displayio_display_construct(displayio_display_obj_t* self, i += 2 + data_size; } - supervisor_start_terminal(width, height); - // Always set the backlight type in case we're reusing memory. self->backlight_inout.base.type = &mp_type_NoneType; if (backlight_pin != NULL && common_hal_mcu_pin_is_free(backlight_pin)) { - pwmout_result_t result = common_hal_pulseio_pwmout_construct(&self->backlight_pwm, backlight_pin, 0, 50000, false); + // Avoid PWM types and functions when the module isn't enabled + #if (CIRCUITPY_PULSEIO) + pwmout_result_t result = common_hal_pwmio_pwmout_construct(&self->backlight_pwm, backlight_pin, 0, 50000, false); if (result != PWMOUT_OK) { self->backlight_inout.base.type = &digitalio_digitalinout_type; common_hal_digitalio_digitalinout_construct(&self->backlight_inout, backlight_pin); common_hal_never_reset_pin(backlight_pin); } else { - self->backlight_pwm.base.type = &pulseio_pwmout_type; - common_hal_pulseio_pwmout_never_reset(&self->backlight_pwm); + self->backlight_pwm.base.type = &pwmio_pwmout_type; + common_hal_pwmio_pwmout_never_reset(&self->backlight_pwm); } + #else + // Otherwise default to digital + self->backlight_inout.base.type = &digitalio_digitalinout_type; + common_hal_digitalio_digitalinout_construct(&self->backlight_inout, backlight_pin); + common_hal_never_reset_pin(backlight_pin); + #endif } if (!self->auto_brightness && (self->backlight_inout.base.type != &mp_type_NoneType || brightness_command != NO_BRIGHTNESS_COMMAND)) { @@ -131,7 +138,7 @@ void common_hal_displayio_display_construct(displayio_display_obj_t* self, // Set the group after initialization otherwise we may send pixels while we delay in // initialization. common_hal_displayio_display_show(self, &circuitpython_splash); - self->auto_refresh = auto_refresh; + common_hal_displayio_display_set_auto_refresh(self, auto_refresh); } bool common_hal_displayio_display_show(displayio_display_obj_t* self, displayio_group_t* root_group) { @@ -164,9 +171,21 @@ bool common_hal_displayio_display_set_brightness(displayio_display_obj_t* self, brightness = 1.0-brightness; } bool ok = false; - if (self->backlight_pwm.base.type == &pulseio_pwmout_type) { - common_hal_pulseio_pwmout_set_duty_cycle(&self->backlight_pwm, (uint16_t) (0xffff * brightness)); + + // Avoid PWM types and functions when the module isn't enabled + #if (CIRCUITPY_PULSEIO) + bool ispwm = (self->backlight_pwm.base.type == &pwmio_pwmout_type) ? true : false; + #else + bool ispwm = false; + #endif + + if (ispwm) { + #if (CIRCUITPY_PULSEIO) + common_hal_pwmio_pwmout_set_duty_cycle(&self->backlight_pwm, (uint16_t) (0xffff * brightness)); ok = true; + #else + ok = false; + #endif } else if (self->backlight_inout.base.type == &digitalio_digitalinout_type) { common_hal_digitalio_digitalinout_set_value(&self->backlight_inout, brightness > 0.99); ok = true; @@ -222,11 +241,17 @@ STATIC bool _refresh_area(displayio_display_obj_t* self, const displayio_area_t* if (!displayio_display_core_clip_area(&self->core, area, &clipped)) { return true; } - uint16_t subrectangles = 1; uint16_t rows_per_buffer = displayio_area_height(&clipped); uint8_t pixels_per_word = (sizeof(uint32_t) * 8) / self->core.colorspace.depth; uint16_t pixels_per_buffer = displayio_area_size(&clipped); - if (displayio_area_size(&clipped) > buffer_size * pixels_per_word) { + + uint16_t subrectangles = 1; + // for SH1107 and other boundary constrained controllers + // write one single row at a time + if (self->SH1107_addressing) { + subrectangles = rows_per_buffer; // vertical (column mode) write each separately (height times) + rows_per_buffer = 1; + } else if (displayio_area_size(&clipped) > buffer_size * pixels_per_word) { rows_per_buffer = buffer_size * pixels_per_word / displayio_area_width(&clipped); if (rows_per_buffer == 0) { rows_per_buffer = 1; @@ -268,7 +293,9 @@ STATIC bool _refresh_area(displayio_display_obj_t* self, const displayio_area_t* } remaining_rows -= rows_per_buffer; - displayio_display_core_set_region_to_update(&self->core, self->set_column_command, self->set_row_command, NO_COMMAND, NO_COMMAND, self->data_as_commands, false, &subrectangle); + displayio_display_core_set_region_to_update(&self->core, self->set_column_command, + self->set_row_command, NO_COMMAND, NO_COMMAND, self->data_as_commands, false, + &subrectangle, self->SH1107_addressing); uint16_t subrectangle_size_bytes; if (self->core.colorspace.depth >= 8) { @@ -299,11 +326,10 @@ STATIC bool _refresh_area(displayio_display_obj_t* self, const displayio_area_t* } STATIC void _refresh_display(displayio_display_obj_t* self) { - if (!displayio_display_core_bus_free(&self->core)) { - // Can't acquire display bus; skip updating this display. Try next display. + if (!displayio_display_core_start_refresh(&self->core)) { + // A refresh on this bus is already in progress. Try next display. return; } - displayio_display_core_start_refresh(&self->core); const displayio_area_t* current_area = _get_refresh_areas(self); while (current_area != NULL) { _refresh_area(self, current_area); @@ -321,8 +347,10 @@ void common_hal_displayio_display_set_rotation(displayio_display_obj_t* self, in self->core.height = tmp; } displayio_display_core_set_rotation(&self->core, rotation); - supervisor_stop_terminal(); - supervisor_start_terminal(self->core.width, self->core.height); + if (self == &displays[0].display) { + supervisor_stop_terminal(); + supervisor_start_terminal(self->core.width, self->core.height); + } if (self->core.current_group != NULL) { displayio_group_update_transform(self->core.current_group, &self->core.transform); } @@ -334,7 +362,7 @@ uint16_t common_hal_displayio_display_get_rotation(displayio_display_obj_t* self bool common_hal_displayio_display_refresh(displayio_display_obj_t* self, uint32_t target_ms_per_frame, uint32_t maximum_ms_per_real_frame) { - if (!self->auto_refresh && !self->first_manual_refresh) { + if (!self->auto_refresh && !self->first_manual_refresh && (target_ms_per_frame != 0xffffffff) ) { uint64_t current_time = supervisor_ticks_ms64(); uint32_t current_ms_since_real_refresh = current_time - self->core.last_refresh; // Test to see if the real frame time is below our minimum. @@ -365,6 +393,13 @@ bool common_hal_displayio_display_get_auto_refresh(displayio_display_obj_t* self void common_hal_displayio_display_set_auto_refresh(displayio_display_obj_t* self, bool auto_refresh) { self->first_manual_refresh = !auto_refresh; + if (auto_refresh != self->auto_refresh) { + if (auto_refresh) { + supervisor_enable_tick(); + } else { + supervisor_disable_tick(); + } + } self->auto_refresh = auto_refresh; } @@ -391,17 +426,22 @@ void displayio_display_background(displayio_display_obj_t* self) { } void release_display(displayio_display_obj_t* self) { + common_hal_displayio_display_set_auto_refresh(self, false); release_display_core(&self->core); - if (self->backlight_pwm.base.type == &pulseio_pwmout_type) { - common_hal_pulseio_pwmout_reset_ok(&self->backlight_pwm); - common_hal_pulseio_pwmout_deinit(&self->backlight_pwm); + #if (CIRCUITPY_PULSEIO) + if (self->backlight_pwm.base.type == &pwmio_pwmout_type) { + common_hal_pwmio_pwmout_reset_ok(&self->backlight_pwm); + common_hal_pwmio_pwmout_deinit(&self->backlight_pwm); } else if (self->backlight_inout.base.type == &digitalio_digitalinout_type) { common_hal_digitalio_digitalinout_deinit(&self->backlight_inout); } + #else + common_hal_digitalio_digitalinout_deinit(&self->backlight_inout); + #endif } void reset_display(displayio_display_obj_t* self) { - self->auto_refresh = true; + common_hal_displayio_display_set_auto_refresh(self, true); self->auto_brightness = true; common_hal_displayio_display_show(self, NULL); } diff --git a/shared-module/displayio/Display.h b/shared-module/displayio/Display.h index 8adaf597f..cc9cd54c4 100644 --- a/shared-module/displayio/Display.h +++ b/shared-module/displayio/Display.h @@ -29,7 +29,9 @@ #include "shared-bindings/digitalio/DigitalInOut.h" #include "shared-bindings/displayio/Group.h" -#include "shared-bindings/pulseio/PWMOut.h" +#if CIRCUITPY_PWMIO +#include "shared-bindings/pwmio/PWMOut.h" +#endif #include "shared-module/displayio/area.h" #include "shared-module/displayio/display_core.h" @@ -39,7 +41,9 @@ typedef struct { displayio_display_core_t core; union { digitalio_digitalinout_obj_t backlight_inout; - pulseio_pwmout_obj_t backlight_pwm; + #if CIRCUITPY_PWMIO + pwmio_pwmout_obj_t backlight_pwm; + #endif }; uint64_t last_backlight_refresh; uint64_t last_refresh_call; @@ -56,6 +60,8 @@ typedef struct { bool auto_brightness; bool updating_backlight; bool backlight_on_high; + // new quirk for sh1107 + bool SH1107_addressing; } displayio_display_obj_t; void displayio_display_background(displayio_display_obj_t* self); diff --git a/shared-module/displayio/EPaperDisplay.c b/shared-module/displayio/EPaperDisplay.c index 5b4a8f916..46c7ea82e 100644 --- a/shared-module/displayio/EPaperDisplay.c +++ b/shared-module/displayio/EPaperDisplay.c @@ -42,8 +42,6 @@ #include <stdint.h> #include <string.h> -#include "tick.h" - void common_hal_displayio_epaperdisplay_construct(displayio_epaperdisplay_obj_t* self, mp_obj_t bus, uint8_t* start_sequence, uint16_t start_sequence_len, uint8_t* stop_sequence, uint16_t stop_sequence_len, uint16_t width, uint16_t height, uint16_t ram_width, uint16_t ram_height, @@ -92,8 +90,6 @@ void common_hal_displayio_epaperdisplay_construct(displayio_epaperdisplay_obj_t* // TODO: Clear } - supervisor_start_terminal(width, height); - // Set the group after initialization otherwise we may send pixels while we delay in // initialization. common_hal_displayio_epaperdisplay_show(self, &circuitpython_splash); @@ -189,6 +185,7 @@ void displayio_epaperdisplay_finish_refresh(displayio_epaperdisplay_obj_t* self) displayio_display_core_begin_transaction(&self->core); self->core.send(self->core.bus, DISPLAY_COMMAND, self->chip_select, &self->refresh_display_command, 1); displayio_display_core_end_transaction(&self->core); + supervisor_enable_tick(); self->refreshing = true; displayio_display_core_finish_refresh(&self->core); @@ -239,8 +236,11 @@ bool displayio_epaperdisplay_refresh_area(displayio_epaperdisplay_obj_t* self, c for (uint8_t pass = 0; pass < passes; pass++) { uint16_t remaining_rows = displayio_area_height(&clipped); + // added false parameter at end for SH1107_addressing quirk if (self->set_row_window_command != NO_COMMAND) { - displayio_display_core_set_region_to_update(&self->core, self->set_column_window_command, self->set_row_window_command, self->set_current_column_command, self->set_current_row_command, false, self->chip_select, &clipped); + displayio_display_core_set_region_to_update(&self->core, self->set_column_window_command, + self->set_row_window_command, self->set_current_column_command, self->set_current_row_command, + false, self->chip_select, &clipped, false); } uint8_t write_command = self->write_black_ram_command; @@ -303,6 +303,7 @@ bool common_hal_displayio_epaperdisplay_refresh(displayio_epaperdisplay_obj_t* s if (self->refreshing && self->busy.base.type == &digitalio_digitalinout_type) { if (common_hal_digitalio_digitalinout_get_value(&self->busy) != self->busy_state) { + supervisor_disable_tick(); self->refreshing = false; // Run stop sequence but don't wait for busy because busy is set when sleeping. send_command_sequence(self, false, self->stop_sequence, self->stop_sequence_len); @@ -344,6 +345,7 @@ void displayio_epaperdisplay_background(displayio_epaperdisplay_obj_t* self) { refresh_done = supervisor_ticks_ms64() - self->core.last_refresh > self->refresh_time; } if (refresh_done) { + supervisor_disable_tick(); self->refreshing = false; // Run stop sequence but don't wait for busy because busy is set when sleeping. send_command_sequence(self, false, self->stop_sequence, self->stop_sequence_len); @@ -354,6 +356,7 @@ void displayio_epaperdisplay_background(displayio_epaperdisplay_obj_t* self) { void release_epaperdisplay(displayio_epaperdisplay_obj_t* self) { if (self->refreshing) { wait_for_busy(self); + supervisor_disable_tick(); self->refreshing = false; // Run stop sequence but don't wait for busy because busy is set when sleeping. send_command_sequence(self, false, self->stop_sequence, self->stop_sequence_len); diff --git a/shared-module/displayio/EPaperDisplay.h b/shared-module/displayio/EPaperDisplay.h index d08bed546..3b9f6e368 100644 --- a/shared-module/displayio/EPaperDisplay.h +++ b/shared-module/displayio/EPaperDisplay.h @@ -29,7 +29,6 @@ #include "shared-bindings/digitalio/DigitalInOut.h" #include "shared-bindings/displayio/Group.h" -#include "shared-bindings/pulseio/PWMOut.h" #include "shared-module/displayio/area.h" #include "shared-module/displayio/display_core.h" diff --git a/shared-module/displayio/FourWire.c b/shared-module/displayio/FourWire.c index fe7234aa7..8c56d7ab6 100644 --- a/shared-module/displayio/FourWire.c +++ b/shared-module/displayio/FourWire.c @@ -36,8 +36,6 @@ #include "shared-bindings/time/__init__.h" #include "shared-module/displayio/display_core.h" -#include "tick.h" - void common_hal_displayio_fourwire_construct(displayio_fourwire_obj_t* self, busio_spi_obj_t* spi, const mcu_pin_obj_t* command, const mcu_pin_obj_t* chip_select, const mcu_pin_obj_t* reset, uint32_t baudrate, @@ -78,7 +76,9 @@ void common_hal_displayio_fourwire_deinit(displayio_fourwire_obj_t* self) { common_hal_reset_pin(self->command.pin); common_hal_reset_pin(self->chip_select.pin); - common_hal_reset_pin(self->reset.pin); + if (self->reset.pin) { + common_hal_reset_pin(self->reset.pin); + } } bool common_hal_displayio_fourwire_reset(mp_obj_t obj) { @@ -87,9 +87,9 @@ bool common_hal_displayio_fourwire_reset(mp_obj_t obj) { return false; } common_hal_digitalio_digitalinout_set_value(&self->reset, false); - common_hal_time_delay_ms(1); + common_hal_mcu_delay_us(1000); common_hal_digitalio_digitalinout_set_value(&self->reset, true); - common_hal_time_delay_ms(1); + common_hal_mcu_delay_us(1000); return true; } diff --git a/shared-module/displayio/Group.c b/shared-module/displayio/Group.c index d69e9f585..15cf5b8e4 100644 --- a/shared-module/displayio/Group.c +++ b/shared-module/displayio/Group.c @@ -29,6 +29,11 @@ #include "py/runtime.h" #include "shared-bindings/displayio/TileGrid.h" +#if CIRCUITPY_VECTORIO +#include "shared-bindings/vectorio/VectorShape.h" +#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) { displayio_group_child_t* children = m_new(displayio_group_child_t, max_size); displayio_group_construct(self, children, max_size, scale, x, y); @@ -117,6 +122,12 @@ static void _update_child_transforms(displayio_group_t* self) { } for (size_t i = 0; i < self->size; i++) { mp_obj_t layer = self->children[i].native; +#if CIRCUITPY_VECTORIO + if (MP_OBJ_IS_TYPE(layer, &vectorio_vector_shape_type)) { + vectorio_vector_shape_update_transform(layer, &self->absolute_transform); + } + else +#endif if (MP_OBJ_IS_TYPE(layer, &displayio_tilegrid_type)) { displayio_tilegrid_update_transform(layer, &self->absolute_transform); } else if (MP_OBJ_IS_TYPE(layer, &displayio_group_type)) { @@ -200,7 +211,15 @@ void common_hal_displayio_group_set_y(displayio_group_t* self, mp_int_t y) { } static mp_obj_t _add_layer(displayio_group_t* self, mp_obj_t layer) { - mp_obj_t native_layer = mp_instance_cast_to_native_base(layer, &displayio_group_type); + 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; + } +#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) { @@ -229,6 +248,14 @@ static void _remove_layer(displayio_group_t* self, size_t index) { mp_obj_t layer = self->children[index].native; displayio_area_t layer_area; bool rendered_last_frame = false; +#if CIRCUITPY_VECTORIO + if (MP_OBJ_IS_TYPE(layer, &vectorio_vector_shape_type)) { + 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)) { displayio_tilegrid_t* tilegrid = layer; rendered_last_frame = displayio_tilegrid_get_previous_area(tilegrid, &layer_area); @@ -317,6 +344,15 @@ bool displayio_group_fill_area(displayio_group_t *self, const _displayio_colorsp bool full_coverage = false; for (int32_t i = self->size - 1; i >= 0 ; i--) { mp_obj_t layer = self->children[i].native; +#if CIRCUITPY_VECTORIO + if (MP_OBJ_IS_TYPE(layer, &vectorio_vector_shape_type)) { + if (vectorio_vector_shape_fill_area(layer, colorspace, area, mask, buffer)) { + full_coverage = true; + break; + } + } + else +#endif if (MP_OBJ_IS_TYPE(layer, &displayio_tilegrid_type)) { if (displayio_tilegrid_fill_area(layer, colorspace, area, mask, buffer)) { full_coverage = true; @@ -336,6 +372,12 @@ 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; +#if CIRCUITPY_VECTORIO + if (MP_OBJ_IS_TYPE(layer, &vectorio_vector_shape_type)) { + vectorio_vector_shape_finish_refresh(layer); + } + else +#endif if (MP_OBJ_IS_TYPE(layer, &displayio_tilegrid_type)) { displayio_tilegrid_finish_refresh(layer); } else if (MP_OBJ_IS_TYPE(layer, &displayio_group_type)) { @@ -352,6 +394,12 @@ 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; +#if CIRCUITPY_VECTORIO + if (MP_OBJ_IS_TYPE(layer, &vectorio_vector_shape_type)) { + tail = vectorio_vector_shape_get_refresh_areas(layer, tail); + } + else +#endif if (MP_OBJ_IS_TYPE(layer, &displayio_tilegrid_type)) { tail = displayio_tilegrid_get_refresh_areas(layer, tail); } else if (MP_OBJ_IS_TYPE(layer, &displayio_group_type)) { diff --git a/shared-module/displayio/I2CDisplay.c b/shared-module/displayio/I2CDisplay.c index 280476f19..5bd03dcd6 100644 --- a/shared-module/displayio/I2CDisplay.c +++ b/shared-module/displayio/I2CDisplay.c @@ -38,8 +38,6 @@ #include "shared-bindings/time/__init__.h" #include "shared-module/displayio/display_core.h" -#include "tick.h" - void common_hal_displayio_i2cdisplay_construct(displayio_i2cdisplay_obj_t* self, busio_i2c_obj_t* i2c, uint16_t device_address, const mcu_pin_obj_t* reset) { @@ -55,6 +53,7 @@ void common_hal_displayio_i2cdisplay_construct(displayio_i2cdisplay_obj_t* self, // Probe the bus to see if a device acknowledges the given address. if (!common_hal_busio_i2c_probe(i2c, device_address)) { + self->base.type = &mp_type_NoneType; mp_raise_ValueError_varg(translate("Unable to find I2C Display at %x"), device_address); } @@ -73,7 +72,9 @@ void common_hal_displayio_i2cdisplay_deinit(displayio_i2cdisplay_obj_t* self) { common_hal_busio_i2c_deinit(self->bus); } - common_hal_reset_pin(self->reset.pin); + if (self->reset.base.type == &digitalio_digitalinout_type) { + common_hal_digitalio_digitalinout_deinit(&self->reset); + } } bool common_hal_displayio_i2cdisplay_reset(mp_obj_t obj) { diff --git a/shared-module/displayio/Palette.c b/shared-module/displayio/Palette.c index 1ef03aab4..facb1fa73 100644 --- a/shared-module/displayio/Palette.c +++ b/shared-module/displayio/Palette.c @@ -35,10 +35,12 @@ void common_hal_displayio_palette_construct(displayio_palette_t* self, uint16_t void common_hal_displayio_palette_make_opaque(displayio_palette_t* self, uint32_t palette_index) { self->colors[palette_index].transparent = false; + self->needs_refresh = true; } void common_hal_displayio_palette_make_transparent(displayio_palette_t* self, uint32_t palette_index) { self->colors[palette_index].transparent = true; + self->needs_refresh = true; } uint32_t common_hal_displayio_palette_get_len(displayio_palette_t* self) { diff --git a/shared-module/displayio/Shape.c b/shared-module/displayio/Shape.c index ab9ca735b..b94a392bc 100644 --- a/shared-module/displayio/Shape.c +++ b/shared-module/displayio/Shape.c @@ -29,6 +29,7 @@ #include <string.h> #include "py/runtime.h" +#include "py/misc.h" void common_hal_displayio_shape_construct(displayio_shape_t *self, uint32_t width, uint32_t height, bool mirror_x, bool mirror_y) { @@ -37,37 +38,77 @@ void common_hal_displayio_shape_construct(displayio_shape_t *self, uint32_t widt self->width = width; if (self->mirror_x) { width /= 2; - width += self->width % 2 - 1; + width += self->width % 2; } self->half_width = width; self->height = height; if (self->mirror_y) { height /= 2; - height += self->height % 2 - 1; + height += self->height % 2; } self->half_height = height; self->data = m_malloc(height * sizeof(uint32_t), false); - for (uint16_t i = 0; i <= height; i++) { + + for (uint16_t i = 0; i < height; i++) { self->data[2 * i] = 0; self->data[2 * i + 1] = width; } + + self->dirty_area.x1=0; + self->dirty_area.x2=width; + self->dirty_area.y1=0; + self->dirty_area.y2=height; } void common_hal_displayio_shape_set_boundary(displayio_shape_t *self, uint16_t y, uint16_t start_x, uint16_t end_x) { - if (y < 0 || y >= self->height || (self->mirror_y && y > self->half_height)) { + if (y < 0 || y >= self->height || (self->mirror_y && y >= self->half_height)) { mp_raise_ValueError(translate("y value out of bounds")); } - if (start_x < 0 || start_x > self->width || end_x < 0 || end_x > self->width) { + if (start_x < 0 || start_x >= self->width || end_x < 0 || end_x >= self->width) { mp_raise_ValueError(translate("x value out of bounds")); } - uint16_t half_width = self->width / 2 - 1 + self->width % 2; - if (self->mirror_x && (start_x > half_width || end_x > half_width)) { - mp_raise_ValueError_varg(translate("Maximum x value when mirrored is %d"), half_width); + if (self->mirror_x && (start_x >= self->half_width || end_x >= self->half_width)) { + mp_raise_ValueError_varg(translate("Maximum x value when mirrored is %d"), self->half_width); + } + + uint16_t lower_x, upper_x, lower_y, upper_y; + + // find x-boundaries for updating based on current data and start_x, end_x, and mirror_x + lower_x = MIN(start_x, self->data[2 * y]); + + if (self->mirror_x) { + upper_x = self->width - lower_x + 1; // dirty rectangles are treated with max value exclusive + } else { + upper_x = MAX(end_x, self->data[2 * y + 1]) + 1; // dirty rectangles are treated with max value exclusive + } + + // find y-boundaries based on y and mirror_y + lower_y = y; + + if (self->mirror_y) { + upper_y = self->height - lower_y + 1; // dirty rectangles are treated with max value exclusive + } else { + upper_y = y + 1; // dirty rectangles are treated with max value exclusive } - self->data[2 * y] = start_x; + + self->data[2 * y] = start_x; // update the data array with the new boundaries self->data[2 * y + 1] = end_x; + + if (self->dirty_area.x1 == self->dirty_area.x2) { // Dirty region is empty + self->dirty_area.x1 = lower_x; + self->dirty_area.x2 = upper_x; + self->dirty_area.y1 = lower_y; + self->dirty_area.y2 = upper_y; + + } else { // Dirty region is not empty + self->dirty_area.x1 = MIN(lower_x, self->dirty_area.x1); + self->dirty_area.x2 = MAX(upper_x, self->dirty_area.x2); + + self->dirty_area.y1 = MIN(lower_y, self->dirty_area.y1); + self->dirty_area.y2 = MAX(upper_y, self->dirty_area.y2); + } } uint32_t common_hal_displayio_shape_get_pixel(void *obj, int16_t x, int16_t y) { @@ -75,10 +116,10 @@ uint32_t common_hal_displayio_shape_get_pixel(void *obj, int16_t x, int16_t y) { if (x >= self->width || x < 0 || y >= self->height || y < 0) { return 0; } - if (self->mirror_x && x > self->half_width) { - x = self->width - 1 - x; + if (self->mirror_x && x >= self->half_width) { + x = self->width - x - 1; } - if (self->mirror_y && y > self->half_height) { + if (self->mirror_y && y >= self->half_height) { y = self->height - y - 1; } uint16_t start_x = self->data[2 * y]; @@ -88,3 +129,16 @@ uint32_t common_hal_displayio_shape_get_pixel(void *obj, int16_t x, int16_t y) { } return 1; } + +displayio_area_t* displayio_shape_get_refresh_areas(displayio_shape_t *self, displayio_area_t* tail) { + if (self->dirty_area.x1 == self->dirty_area.x2) { + return tail; + } + self->dirty_area.next = tail; + return &self->dirty_area; +} + +void displayio_shape_finish_refresh(displayio_shape_t *self) { + self->dirty_area.x1 = 0; + self->dirty_area.x2 = 0; +} diff --git a/shared-module/displayio/Shape.h b/shared-module/displayio/Shape.h index ca054fe00..e59ad586e 100644 --- a/shared-module/displayio/Shape.h +++ b/shared-module/displayio/Shape.h @@ -31,6 +31,7 @@ #include <stdint.h> #include "py/obj.h" +#include "shared-module/displayio/area.h" typedef struct { mp_obj_base_t base; @@ -41,6 +42,10 @@ typedef struct { uint16_t* data; bool mirror_x; bool mirror_y; + displayio_area_t dirty_area; } displayio_shape_t; +void displayio_shape_finish_refresh(displayio_shape_t *self); +displayio_area_t* displayio_shape_get_refresh_areas(displayio_shape_t *self, displayio_area_t* tail); + #endif // MICROPY_INCLUDED_SHARED_MODULE_DISPLAYIO_SHAPE_H diff --git a/shared-module/displayio/TileGrid.c b/shared-module/displayio/TileGrid.c index c4855c333..e3642107f 100644 --- a/shared-module/displayio/TileGrid.c +++ b/shared-module/displayio/TileGrid.c @@ -435,7 +435,7 @@ bool displayio_tilegrid_fill_area(displayio_tilegrid_t *self, const _displayio_c } else if (MP_OBJ_IS_TYPE(self->bitmap, &displayio_ondiskbitmap_type)) { input_pixel.pixel = common_hal_displayio_ondiskbitmap_get_pixel(self->bitmap, input_pixel.tile_x, input_pixel.tile_y); } - + output_pixel.opaque = true; if (self->pixel_shader == mp_const_none) { output_pixel.pixel = input_pixel.pixel; @@ -499,7 +499,7 @@ void displayio_tilegrid_finish_refresh(displayio_tilegrid_t *self) { if (MP_OBJ_IS_TYPE(self->bitmap, &displayio_bitmap_type)) { displayio_bitmap_finish_refresh(self->bitmap); } else if (MP_OBJ_IS_TYPE(self->bitmap, &displayio_shape_type)) { - // TODO: Support shape changes. + displayio_shape_finish_refresh(self->bitmap); } else if (MP_OBJ_IS_TYPE(self->bitmap, &displayio_ondiskbitmap_type)) { // OnDiskBitmap changes will trigger a complete reload so no need to // track changes. @@ -543,6 +543,12 @@ displayio_area_t* displayio_tilegrid_get_refresh_areas(displayio_tilegrid_t *sel self->full_change = true; } } + } else if (MP_OBJ_IS_TYPE(self->bitmap, &displayio_shape_type)) { + displayio_area_t* refresh_area = displayio_shape_get_refresh_areas(self->bitmap, tail); + if (refresh_area != tail) { + displayio_area_copy(refresh_area, &self->dirty_area); + self->partial_change = true; + } } self->full_change = self->full_change || diff --git a/shared-module/displayio/__init__.c b/shared-module/displayio/__init__.c index c898bbb98..a9bb3b21b 100644 --- a/shared-module/displayio/__init__.c +++ b/shared-module/displayio/__init__.c @@ -19,14 +19,19 @@ #include "supervisor/spi_flash_api.h" #include "py/mpconfig.h" +#if CIRCUITPY_SHARPDISPLAY +#include "shared-bindings/sharpdisplay/SharpMemoryFramebuffer.h" +#include "shared-module/sharpdisplay/SharpMemoryFramebuffer.h" +#endif + primary_display_t displays[CIRCUITPY_DISPLAY_LIMIT]; #if CIRCUITPY_RGBMATRIX -STATIC bool any_display_uses_this_rgbmatrix(rgbmatrix_rgbmatrix_obj_t* pm) { +STATIC bool any_display_uses_this_framebuffer(mp_obj_base_t *obj) { for (uint8_t i = 0; i < CIRCUITPY_DISPLAY_LIMIT; i++) { - if (displays[i].framebuffer_display.base.type == &framebufferio_framebufferdisplay_type) { + if (displays[i].display_base.type == &framebufferio_framebufferdisplay_type) { framebufferio_framebufferdisplay_obj_t* display = &displays[i].framebuffer_display; - if (display->framebuffer == pm) { + if (display->framebuffer == obj) { return true; } } @@ -35,8 +40,6 @@ STATIC bool any_display_uses_this_rgbmatrix(rgbmatrix_rgbmatrix_obj_t* pm) { } #endif -// Check for recursive calls to displayio_background. -bool displayio_background_in_progress = false; void displayio_background(void) { if (mp_hal_is_interrupted()) { @@ -47,12 +50,6 @@ void displayio_background(void) { return; } - if (displayio_background_in_progress) { - // Don't allow recursive calls to this routine. - return; - } - - displayio_background_in_progress = true; for (uint8_t i = 0; i < CIRCUITPY_DISPLAY_LIMIT; i++) { if (displays[i].display.base.type == NULL || displays[i].display.base.type == &mp_type_NoneType) { @@ -70,8 +67,6 @@ void displayio_background(void) { } } - // All done. - displayio_background_in_progress = false; } void common_hal_displayio_release_displays(void) { @@ -102,10 +97,14 @@ void common_hal_displayio_release_displays(void) { common_hal_displayio_i2cdisplay_deinit(&displays[i].i2cdisplay_bus); } else if (bus_type == &displayio_parallelbus_type) { common_hal_displayio_parallelbus_deinit(&displays[i].parallel_bus); -#if CIRCUITPY_FRAMEBUFFERIO +#if CIRCUITPY_RGBMATRIX } else if (bus_type == &rgbmatrix_RGBMatrix_type) { common_hal_rgbmatrix_rgbmatrix_deinit(&displays[i].rgbmatrix); #endif +#if CIRCUITPY_SHARPDISPLAY + } else if (displays[i].bus_base.type == &sharpdisplay_framebuffer_type) { + common_hal_sharpdisplay_framebuffer_deinit(&displays[i].sharpdisplay); +#endif } displays[i].fourwire_bus.base.type = &mp_type_NoneType; } @@ -170,10 +169,15 @@ void reset_displays(void) { #if CIRCUITPY_RGBMATRIX } else if (displays[i].rgbmatrix.base.type == &rgbmatrix_RGBMatrix_type) { rgbmatrix_rgbmatrix_obj_t * pm = &displays[i].rgbmatrix; - if(!any_display_uses_this_rgbmatrix(pm)) { + if(!any_display_uses_this_framebuffer(&pm->base)) { common_hal_rgbmatrix_rgbmatrix_deinit(pm); } #endif +#if CIRCUITPY_SHARPDISPLAY + } else if (displays[i].bus_base.type == &sharpdisplay_framebuffer_type) { + sharpdisplay_framebuffer_obj_t * sharp = &displays[i].sharpdisplay; + common_hal_sharpdisplay_framebuffer_reset(sharp); +#endif } else { // Not an active display bus. continue; @@ -190,9 +194,7 @@ void reset_displays(void) { common_hal_displayio_epaperdisplay_show(display, NULL); #if CIRCUITPY_FRAMEBUFFERIO } else if (displays[i].framebuffer_display.base.type == &framebufferio_framebufferdisplay_type) { - framebufferio_framebufferdisplay_obj_t* display = &displays[i].framebuffer_display; - display->auto_refresh = true; - common_hal_framebufferio_framebufferdisplay_show(display, NULL); + framebufferio_framebufferdisplay_reset(&displays[i].framebuffer_display); #endif } } @@ -205,6 +207,11 @@ void displayio_gc_collect(void) { rgbmatrix_rgbmatrix_collect_ptrs(&displays[i].rgbmatrix); } #endif +#if CIRCUITPY_SHARPDISPLAY + if (displays[i].bus_base.type == &sharpdisplay_framebuffer_type) { + common_hal_sharpdisplay_framebuffer_collect_ptrs(&displays[i].sharpdisplay); + } +#endif if (displays[i].display.base.type == NULL) { continue; @@ -377,8 +384,8 @@ primary_display_t *allocate_display_or_raise(void) { } primary_display_t *allocate_display_bus(void) { for (uint8_t i = 0; i < CIRCUITPY_DISPLAY_LIMIT; i++) { - mp_const_obj_t display_type = displays[i].display.base.type; - if (display_type == NULL || display_type == &mp_type_NoneType) { + mp_const_obj_t display_bus_type = displays[i].bus_base.type; + if (display_bus_type == NULL || display_bus_type == &mp_type_NoneType) { return &displays[i]; } } diff --git a/shared-module/displayio/__init__.h b/shared-module/displayio/__init__.h index 9eb10c28b..44ad5a9a9 100644 --- a/shared-module/displayio/__init__.h +++ b/shared-module/displayio/__init__.h @@ -36,18 +36,28 @@ #include "shared-bindings/displayio/Group.h" #include "shared-bindings/displayio/I2CDisplay.h" #include "shared-bindings/displayio/ParallelBus.h" +#if CIRCUITPY_RGBMATRIX #include "shared-bindings/rgbmatrix/RGBMatrix.h" +#endif +#if CIRCUITPY_SHARPDISPLAY +#include "shared-module/sharpdisplay/SharpMemoryFramebuffer.h" +#endif typedef struct { union { + mp_obj_base_t bus_base; displayio_fourwire_obj_t fourwire_bus; displayio_i2cdisplay_obj_t i2cdisplay_bus; displayio_parallelbus_obj_t parallel_bus; #if CIRCUITPY_RGBMATRIX rgbmatrix_rgbmatrix_obj_t rgbmatrix; #endif +#if CIRCUITPY_SHARPDISPLAY + sharpdisplay_framebuffer_obj_t sharpdisplay; +#endif }; union { + mp_obj_base_t display_base; displayio_display_obj_t display; displayio_epaperdisplay_obj_t epaper_display; #if CIRCUITPY_FRAMEBUFFERIO diff --git a/shared-module/displayio/display_core.c b/shared-module/displayio/display_core.c index 4cc7564d0..411f9f373 100644 --- a/shared-module/displayio/display_core.c +++ b/shared-module/displayio/display_core.c @@ -40,7 +40,8 @@ #include <stdint.h> #include <string.h> -#include "tick.h" +#define DISPLAYIO_CORE_DEBUG(...) (void)0 +// #define DISPLAYIO_CORE_DEBUG(...) mp_printf(&mp_plat_print __VA_OPT__(,) __VA_ARGS__) void displayio_display_core_construct(displayio_display_core_t* self, mp_obj_t bus, uint16_t width, uint16_t height, uint16_t ram_width, uint16_t ram_height, int16_t colstart, int16_t rowstart, uint16_t rotation, @@ -84,7 +85,10 @@ void displayio_display_core_construct(displayio_display_core_t* self, self->bus = bus; - supervisor_start_terminal(width, height); + // (offsetof core is equal in all display types) + if (self == &displays[0].display.core) { + supervisor_start_terminal(width, height); + } self->width = width; self->height = height; @@ -196,7 +200,7 @@ bool displayio_display_core_get_dither(displayio_display_core_t* self){ } bool displayio_display_core_bus_free(displayio_display_core_t *self) { - return self->bus_free(self->bus); + return !self->bus || self->bus_free(self->bus); } bool displayio_display_core_begin_transaction(displayio_display_core_t* self) { @@ -207,7 +211,10 @@ void displayio_display_core_end_transaction(displayio_display_core_t* self) { self->end_transaction(self->bus); } -void displayio_display_core_set_region_to_update(displayio_display_core_t* self, uint8_t column_command, uint8_t row_command, uint16_t set_current_column_command, uint16_t set_current_row_command, bool data_as_commands, bool always_toggle_chip_select, displayio_area_t* area) { +void displayio_display_core_set_region_to_update(displayio_display_core_t* self, uint8_t column_command, + uint8_t row_command, uint16_t set_current_column_command, uint16_t set_current_row_command, + bool data_as_commands, bool always_toggle_chip_select, + displayio_area_t* area, bool SH1107_addressing) { uint16_t x1 = area->x1; uint16_t x2 = area->x2; uint16_t y1 = area->y1; @@ -252,8 +259,17 @@ void displayio_display_core_set_region_to_update(displayio_display_core_t* self, data[data_length++] = x2 >> 8; data[data_length++] = x2 & 0xff; } + // Quirk for SH1107 "SH1107_addressing" + // Note... column is y! page is x! + // Page address command = 0xB0 + if (SH1107_addressing) { + // set the page to our x value + data[0] = 0xB0 | (x1 & 0x0F); + data_length = 1; + } self->send(self->bus, data_type, chip_select, data, data_length); displayio_display_core_end_transaction(self); + if (set_current_column_command != NO_COMMAND) { uint8_t command = set_current_column_command; displayio_display_core_begin_transaction(self); @@ -282,7 +298,16 @@ void displayio_display_core_set_region_to_update(displayio_display_core_t* self, data[data_length++] = y2 >> 8; data[data_length++] = y2 & 0xff; } + // Quirk for SH1107 "SH1107_addressing" + // Note... column is y! page is x! + // Column lower command = 0x00, Column upper command = 0x10 + if (SH1107_addressing) { + data[0] = y1 & 0x0F; // 0x00 to 0x0F + data[1] = (y1 >> 4 & 0x0F) | 0x10; // 0x10 to 0x17 + data_length = 2; + } self->send(self->bus, data_type, chip_select, data, data_length); + displayio_display_core_end_transaction(self); if (set_current_row_command != NO_COMMAND) { @@ -294,15 +319,26 @@ void displayio_display_core_set_region_to_update(displayio_display_core_t* self, } } -void displayio_display_core_start_refresh(displayio_display_core_t* self) { +bool displayio_display_core_start_refresh(displayio_display_core_t* self) { + if (!displayio_display_core_bus_free(self)) { + // Can't acquire display bus; skip updating this display. Try next display. + return false; + } + if (self->refresh_in_progress) { + return false; + } + self->refresh_in_progress = true; self->last_refresh = supervisor_ticks_ms64(); + return true; } void displayio_display_core_finish_refresh(displayio_display_core_t* self) { if (self->current_group != NULL) { + DISPLAYIO_CORE_DEBUG("displayiocore group_finish_refresh\n"); displayio_group_finish_refresh(self->current_group); } self->full_refresh = false; + self->refresh_in_progress = false; self->last_refresh = supervisor_ticks_ms64(); } diff --git a/shared-module/displayio/display_core.h b/shared-module/displayio/display_core.h index e4fd62d4a..fe6cb6f3f 100644 --- a/shared-module/displayio/display_core.h +++ b/shared-module/displayio/display_core.h @@ -54,6 +54,7 @@ typedef struct { int16_t colstart; int16_t rowstart; bool full_refresh; // New group means we need to refresh the whole display. + bool refresh_in_progress; } displayio_display_core_t; void displayio_display_core_construct(displayio_display_core_t* self, @@ -74,11 +75,14 @@ bool displayio_display_core_bus_free(displayio_display_core_t *self); bool displayio_display_core_begin_transaction(displayio_display_core_t* self); void displayio_display_core_end_transaction(displayio_display_core_t* self); -void displayio_display_core_set_region_to_update(displayio_display_core_t* self, uint8_t column_command, uint8_t row_command, uint16_t set_current_column_command, uint16_t set_current_row_command, bool data_as_commands, bool always_toggle_chip_select, displayio_area_t* area); +void displayio_display_core_set_region_to_update(displayio_display_core_t* self, uint8_t column_command, + uint8_t row_command, uint16_t set_current_column_command, uint16_t set_current_row_command, + bool data_as_commands, bool always_toggle_chip_select, + displayio_area_t* area, bool SH1107_addressing); void release_display_core(displayio_display_core_t* self); -void displayio_display_core_start_refresh(displayio_display_core_t* self); +bool displayio_display_core_start_refresh(displayio_display_core_t* self); void displayio_display_core_finish_refresh(displayio_display_core_t* self); void displayio_display_core_collect_ptrs(displayio_display_core_t* self); diff --git a/shared-module/framebufferio/FramebufferDisplay.c b/shared-module/framebufferio/FramebufferDisplay.c index 13fbb2629..03e121c91 100644 --- a/shared-module/framebufferio/FramebufferDisplay.c +++ b/shared-module/framebufferio/FramebufferDisplay.c @@ -40,7 +40,10 @@ #include <stdint.h> #include <string.h> -#include "tick.h" +#define fb_getter_default(method, default_value) \ + (self->framebuffer_protocol->method \ + ? self->framebuffer_protocol->method(self->framebuffer) \ + : (default_value)) void common_hal_framebufferio_framebufferdisplay_construct(framebufferio_framebufferdisplay_obj_t* self, mp_obj_t framebuffer, @@ -53,35 +56,50 @@ void common_hal_framebufferio_framebufferdisplay_construct(framebufferio_framebu uint16_t ram_width = 0x100; uint16_t ram_height = 0x100; - + uint16_t depth = fb_getter_default(get_color_depth, 16); displayio_display_core_construct( &self->core, NULL, self->framebuffer_protocol->get_width(self->framebuffer), self->framebuffer_protocol->get_height(self->framebuffer), - ram_width, + ram_width, ram_height, 0, 0, - rotation, - self->framebuffer_protocol->get_color_depth(self->framebuffer), - false, - false, - self->framebuffer_protocol->get_bytes_per_cell(self->framebuffer), - false, - false); + 0, // rotation + depth, + fb_getter_default(get_grayscale, (depth < 8)), + fb_getter_default(get_pixels_in_byte_share_row, false), + fb_getter_default(get_bytes_per_cell, 2), + fb_getter_default(get_reverse_pixels_in_byte, false), + fb_getter_default(get_reverse_pixels_in_word, false) + ); + + self->first_pixel_offset = fb_getter_default(get_first_pixel_offset, 0); + self->row_stride = fb_getter_default(get_row_stride, 0); + if (self->row_stride == 0) { + self->row_stride = self->core.width * self->core.colorspace.depth/8; + } + + self->framebuffer_protocol->get_bufinfo(self->framebuffer, &self->bufinfo); + size_t framebuffer_size = self->first_pixel_offset + self->row_stride * self->core.height; + if (self->bufinfo.len < framebuffer_size) { + mp_raise_IndexError_varg(translate("Framebuffer requires %d bytes"), framebuffer_size); + } self->first_manual_refresh = !auto_refresh; - self->native_frames_per_second = self->framebuffer_protocol->get_native_frames_per_second(self->framebuffer); + self->native_frames_per_second = fb_getter_default(get_native_frames_per_second, 60); self->native_ms_per_frame = 1000 / self->native_frames_per_second; - supervisor_start_terminal(self->core.width, self->core.height); + if (rotation != 0) { + common_hal_framebufferio_framebufferdisplay_set_rotation(self, rotation); + } // Set the group after initialization otherwise we may send pixels while we delay in // initialization. common_hal_framebufferio_framebufferdisplay_show(self, &circuitpython_splash); - self->auto_refresh = auto_refresh; + common_hal_framebufferio_framebufferdisplay_set_auto_refresh(self, auto_refresh); } bool common_hal_framebufferio_framebufferdisplay_show(framebufferio_framebufferdisplay_obj_t* self, displayio_group_t* root_group) { @@ -111,7 +129,7 @@ bool common_hal_framebufferio_framebufferdisplay_set_auto_brightness(framebuffer } mp_float_t common_hal_framebufferio_framebufferdisplay_get_brightness(framebufferio_framebufferdisplay_obj_t* self) { - if (self->framebuffer_protocol->set_brightness) { + if (self->framebuffer_protocol->get_brightness) { return self->framebuffer_protocol->get_brightness(self->framebuffer); } return -1; @@ -140,7 +158,8 @@ STATIC const displayio_area_t* _get_refresh_areas(framebufferio_framebufferdispl return NULL; } -STATIC bool _refresh_area(framebufferio_framebufferdisplay_obj_t* self, const displayio_area_t* area) { +#define MARK_ROW_DIRTY(r) (dirty_row_bitmask[r/8] |= (1 << (r & 7))) +STATIC bool _refresh_area(framebufferio_framebufferdisplay_obj_t* self, const displayio_area_t* area, uint8_t *dirty_row_bitmask) { uint16_t buffer_size = 128; // In uint32_ts displayio_area_t clipped; @@ -149,6 +168,14 @@ STATIC bool _refresh_area(framebufferio_framebufferdisplay_obj_t* self, const di return true; } uint16_t subrectangles = 1; + + // If pixels are packed by row then rows are on byte boundaries + if (self->core.colorspace.depth < 8 && self->core.colorspace.pixels_in_byte_share_row) { + int div = 8 / self->core.colorspace.depth; + clipped.x1 = (clipped.x1 / div) * div; + clipped.x2 = ((clipped.x2 + div - 1) / div) * div; + } + uint16_t rows_per_buffer = displayio_area_height(&clipped); uint8_t pixels_per_word = (sizeof(uint32_t) * 8) / self->core.colorspace.depth; uint16_t pixels_per_buffer = displayio_area_size(&clipped); @@ -189,6 +216,7 @@ STATIC bool _refresh_area(framebufferio_framebufferdisplay_obj_t* self, const di .x2 = clipped.x2, .y2 = clipped.y1 + rows_per_buffer * (j + 1) }; + if (remaining_rows < rows_per_buffer) { subrectangle.y2 = subrectangle.y1 + remaining_rows; } @@ -199,12 +227,18 @@ STATIC bool _refresh_area(framebufferio_framebufferdisplay_obj_t* self, const di displayio_display_core_fill_area(&self->core, &subrectangle, mask, buffer); - // COULDDO: this arithmetic only supports multiple-of-8 bpp - uint8_t *dest = self->bufinfo.buf + (subrectangle.y1 * self->core.width + subrectangle.x1) * (self->core.colorspace.depth / 8); + uint8_t *buf = (uint8_t *)self->bufinfo.buf, *endbuf = buf + self->bufinfo.len; + (void)endbuf; // Hint to compiler that endbuf is "used" even if NDEBUG + buf += self->first_pixel_offset; + + size_t rowstride = self->row_stride; + uint8_t *dest = buf + subrectangle.y1 * rowstride + subrectangle.x1 * self->core.colorspace.depth / 8; uint8_t *src = (uint8_t*)buffer; - size_t rowsize = (subrectangle.x2 - subrectangle.x1) * (self->core.colorspace.depth / 8); - size_t rowstride = self->core.width * (self->core.colorspace.depth/8); + size_t rowsize = (subrectangle.x2 - subrectangle.x1) * self->core.colorspace.depth / 8; + for (uint16_t i = subrectangle.y1; i < subrectangle.y2; i++) { + assert(dest >= buf && dest < endbuf && dest+rowsize <= endbuf); + MARK_ROW_DIRTY(i); memcpy(dest, src, rowsize); dest += rowstride; src += rowsize; @@ -218,15 +252,23 @@ STATIC bool _refresh_area(framebufferio_framebufferdisplay_obj_t* self, const di } STATIC void _refresh_display(framebufferio_framebufferdisplay_obj_t* self) { - displayio_display_core_start_refresh(&self->core); self->framebuffer_protocol->get_bufinfo(self->framebuffer, &self->bufinfo); + if(!self->bufinfo.buf) { + return; + } + displayio_display_core_start_refresh(&self->core); const displayio_area_t* current_area = _get_refresh_areas(self); - while (current_area != NULL) { - _refresh_area(self, current_area); - current_area = current_area->next; + if (current_area) { + uint8_t dirty_row_bitmask[(self->core.height + 7) / 8]; + memset(dirty_row_bitmask, 0, sizeof(dirty_row_bitmask)); + self->framebuffer_protocol->get_bufinfo(self->framebuffer, &self->bufinfo); + while (current_area != NULL) { + _refresh_area(self, current_area, dirty_row_bitmask); + current_area = current_area->next; + } + self->framebuffer_protocol->swapbuffers(self->framebuffer, dirty_row_bitmask); } displayio_display_core_finish_refresh(&self->core); - self->framebuffer_protocol->swapbuffers(self->framebuffer); } void common_hal_framebufferio_framebufferdisplay_set_rotation(framebufferio_framebufferdisplay_obj_t* self, int rotation){ @@ -238,8 +280,10 @@ void common_hal_framebufferio_framebufferdisplay_set_rotation(framebufferio_fram self->core.height = tmp; } displayio_display_core_set_rotation(&self->core, rotation); - supervisor_stop_terminal(); - supervisor_start_terminal(self->core.width, self->core.height); + if (self == &displays[0].framebuffer_display) { + supervisor_stop_terminal(); + supervisor_start_terminal(self->core.width, self->core.height); + } if (self->core.current_group != NULL) { displayio_group_update_transform(self->core.current_group, &self->core.transform); } @@ -282,6 +326,13 @@ bool common_hal_framebufferio_framebufferdisplay_get_auto_refresh(framebufferio_ void common_hal_framebufferio_framebufferdisplay_set_auto_refresh(framebufferio_framebufferdisplay_obj_t* self, bool auto_refresh) { self->first_manual_refresh = !auto_refresh; + if (auto_refresh != self->auto_refresh) { + if (auto_refresh) { + supervisor_enable_tick(); + } else { + supervisor_disable_tick(); + } + } self->auto_refresh = auto_refresh; } @@ -299,16 +350,24 @@ void framebufferio_framebufferdisplay_background(framebufferio_framebufferdispla } void release_framebufferdisplay(framebufferio_framebufferdisplay_obj_t* self) { + common_hal_framebufferio_framebufferdisplay_set_auto_refresh(self, false); release_display_core(&self->core); self->framebuffer_protocol->deinit(self->framebuffer); -} - -void reset_framebufferdisplay(framebufferio_framebufferdisplay_obj_t* self) { - self->auto_refresh = true; - common_hal_framebufferio_framebufferdisplay_show(self, NULL); + self->base.type = &mp_type_NoneType; } void framebufferio_framebufferdisplay_collect_ptrs(framebufferio_framebufferdisplay_obj_t* self) { gc_collect_ptr(self->framebuffer); displayio_display_core_collect_ptrs(&self->core); } + +void framebufferio_framebufferdisplay_reset(framebufferio_framebufferdisplay_obj_t* self) { + mp_obj_type_t *fb_type = mp_obj_get_type(self->framebuffer); + if(fb_type != NULL && fb_type != &mp_type_NoneType) { + common_hal_framebufferio_framebufferdisplay_set_auto_refresh(self, true); + common_hal_framebufferio_framebufferdisplay_show(self, NULL); + self->core.full_refresh = true; + } else { + release_framebufferdisplay(self); + } +} diff --git a/shared-module/framebufferio/FramebufferDisplay.h b/shared-module/framebufferio/FramebufferDisplay.h index 1b68d2ab0..d73d4b9a9 100644 --- a/shared-module/framebufferio/FramebufferDisplay.h +++ b/shared-module/framebufferio/FramebufferDisplay.h @@ -33,7 +33,6 @@ #include "shared-bindings/digitalio/DigitalInOut.h" #include "shared-bindings/displayio/Group.h" -#include "shared-bindings/pulseio/PWMOut.h" #include "shared-module/displayio/area.h" #include "shared-module/displayio/display_core.h" @@ -48,43 +47,65 @@ typedef struct { uint64_t last_refresh_call; uint16_t native_frames_per_second; uint16_t native_ms_per_frame; + uint16_t first_pixel_offset; + uint16_t row_stride; bool auto_refresh; bool first_manual_refresh; } framebufferio_framebufferdisplay_obj_t; void framebufferio_framebufferdisplay_background(framebufferio_framebufferdisplay_obj_t* self); void release_framebufferdisplay(framebufferio_framebufferdisplay_obj_t* self); -void reset_framebufferdisplay(framebufferio_framebufferdisplay_obj_t* self); +void framebufferio_framebufferdisplay_reset(framebufferio_framebufferdisplay_obj_t* self); void framebufferio_framebufferdisplay_collect_ptrs(framebufferio_framebufferdisplay_obj_t* self); mp_obj_t common_hal_framebufferio_framebufferdisplay_get_framebuffer(framebufferio_framebufferdisplay_obj_t* self); -typedef void (*framebuffer_get_bufinfo_fun)(mp_obj_t, mp_buffer_info_t *bufinfo); -typedef void (*framebuffer_swapbuffers_fun)(mp_obj_t); -typedef void (*framebuffer_deinit_fun)(mp_obj_t); -typedef bool (*framebuffer_set_brightness_fun)(mp_obj_t, mp_float_t); -typedef mp_float_t (*framebuffer_get_brightness_fun)(mp_obj_t); -typedef bool (*framebuffer_set_auto_brightness_fun)(mp_obj_t, bool); typedef bool (*framebuffer_get_auto_brightness_fun)(mp_obj_t); -typedef int (*framebuffer_get_width_fun)(mp_obj_t); -typedef int (*framebuffer_get_height_fun)(mp_obj_t); -typedef int (*framebuffer_get_color_depth_fun)(mp_obj_t); +typedef bool (*framebuffer_get_reverse_pixels_in_byte_fun)(mp_obj_t); +typedef bool (*framebuffer_get_reverse_pixels_in_word_fun)(mp_obj_t); +typedef bool (*framebuffer_set_auto_brightness_fun)(mp_obj_t, bool); +typedef bool (*framebuffer_set_brightness_fun)(mp_obj_t, mp_float_t); typedef int (*framebuffer_get_bytes_per_cell_fun)(mp_obj_t); +typedef int (*framebuffer_get_color_depth_fun)(mp_obj_t); +typedef int (*framebuffer_get_first_pixel_offset_fun)(mp_obj_t); +typedef int (*framebuffer_get_grayscale_fun)(mp_obj_t); +typedef int (*framebuffer_get_height_fun)(mp_obj_t); typedef int (*framebuffer_get_native_frames_per_second_fun)(mp_obj_t); +typedef bool (*framebuffer_get_pixels_in_byte_share_row_fun)(mp_obj_t); +typedef int (*framebuffer_get_row_stride_fun)(mp_obj_t); +typedef int (*framebuffer_get_width_fun)(mp_obj_t); +typedef mp_float_t (*framebuffer_get_brightness_fun)(mp_obj_t); +typedef void (*framebuffer_deinit_fun)(mp_obj_t); +typedef void (*framebuffer_get_bufinfo_fun)(mp_obj_t, mp_buffer_info_t *bufinfo); +typedef void (*framebuffer_swapbuffers_fun)(mp_obj_t, uint8_t *dirty_row_bitmask); typedef struct _framebuffer_p_t { MP_PROTOCOL_HEAD // MP_QSTR_protocol_framebuffer + + // Mandatory framebuffer_get_bufinfo_fun get_bufinfo; framebuffer_swapbuffers_fun swapbuffers; framebuffer_deinit_fun deinit; framebuffer_get_width_fun get_width; framebuffer_get_height_fun get_height; - framebuffer_get_color_depth_fun get_color_depth; - framebuffer_get_bytes_per_cell_fun get_bytes_per_cell; - framebuffer_get_native_frames_per_second_fun get_native_frames_per_second; + + // Optional getters + framebuffer_get_bytes_per_cell_fun get_bytes_per_cell; // default: 2 + framebuffer_get_color_depth_fun get_color_depth; // default: 16 + framebuffer_get_first_pixel_offset_fun get_first_pixel_offset; // default: 0 + framebuffer_get_grayscale_fun get_grayscale; // default: grayscale if depth < 8 + framebuffer_get_native_frames_per_second_fun get_native_frames_per_second; // default: 60 + framebuffer_get_pixels_in_byte_share_row_fun get_pixels_in_byte_share_row; // default: false + framebuffer_get_reverse_pixels_in_byte_fun get_reverse_pixels_in_byte; // default: false + framebuffer_get_reverse_pixels_in_word_fun get_reverse_pixels_in_word; // default: false + framebuffer_get_row_stride_fun get_row_stride; // default: 0 (no extra row padding) + + // Optional -- default is no brightness control framebuffer_get_brightness_fun get_brightness; framebuffer_set_brightness_fun set_brightness; + + // Optional -- default is no automatic brightness control framebuffer_get_auto_brightness_fun get_auto_brightness; framebuffer_set_auto_brightness_fun set_auto_brightness; } framebuffer_p_t; diff --git a/shared-module/gamepad/GamePad.c b/shared-module/gamepad/GamePad.c index fdce0caab..7b4108eb2 100644 --- a/shared-module/gamepad/GamePad.c +++ b/shared-module/gamepad/GamePad.c @@ -27,6 +27,7 @@ #include "py/mpstate.h" #include "shared-bindings/digitalio/DigitalInOut.h" #include "shared-bindings/gamepad/GamePad.h" +#include "supervisor/shared/tick.h" void common_hal_gamepad_gamepad_init(gamepad_obj_t *gamepad, const mp_obj_t pins[], size_t n_pins) { @@ -54,4 +55,5 @@ void common_hal_gamepad_gamepad_init(gamepad_obj_t *gamepad, void common_hal_gamepad_gamepad_deinit(gamepad_obj_t *self) { MP_STATE_VM(gamepad_singleton) = NULL; + supervisor_disable_tick(); } diff --git a/shared-module/gamepad/__init__.c b/shared-module/gamepad/__init__.c index 3e17a0fb5..9874b2752 100644 --- a/shared-module/gamepad/__init__.c +++ b/shared-module/gamepad/__init__.c @@ -29,6 +29,7 @@ #include "py/mpstate.h" #include "shared-bindings/gamepad/__init__.h" #include "shared-bindings/gamepad/GamePad.h" +#include "supervisor/shared/tick.h" #include "shared-bindings/digitalio/DigitalInOut.h" @@ -59,5 +60,8 @@ void gamepad_tick(void) { } void gamepad_reset(void) { + if (MP_STATE_VM(gamepad_singleton)) { + supervisor_disable_tick(); + } MP_STATE_VM(gamepad_singleton) = NULL; } diff --git a/shared-module/gamepadshift/GamePadShift.c b/shared-module/gamepadshift/GamePadShift.c index b8fb4d3f4..51da61702 100644 --- a/shared-module/gamepadshift/GamePadShift.c +++ b/shared-module/gamepadshift/GamePadShift.c @@ -27,6 +27,7 @@ #include "py/mpstate.h" #include "shared-bindings/digitalio/DigitalInOut.h" #include "shared-module/gamepadshift/GamePadShift.h" +#include "supervisor/shared/tick.h" void common_hal_gamepadshift_gamepadshift_init(gamepadshift_obj_t *gamepadshift, digitalio_digitalinout_obj_t *clock_pin, @@ -46,4 +47,5 @@ void common_hal_gamepadshift_gamepadshift_init(gamepadshift_obj_t *gamepadshift, void common_hal_gamepadshift_gamepadshift_deinit(gamepadshift_obj_t *gamepadshift) { MP_STATE_VM(gamepad_singleton) = NULL; + supervisor_disable_tick(); } diff --git a/shared-module/ipaddress/IPv4Address.c b/shared-module/ipaddress/IPv4Address.c new file mode 100644 index 000000000..f573d9d0a --- /dev/null +++ b/shared-module/ipaddress/IPv4Address.c @@ -0,0 +1,39 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2020 Scott Shawcroft for Adafruit Industries + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "py/obj.h" + +#include "shared-bindings/ipaddress/__init__.h" +#include "shared-bindings/ipaddress/IPv4Address.h" + + +void common_hal_ipaddress_ipv4address_construct(ipaddress_ipv4address_obj_t* self, uint8_t* buf, size_t len) { + self->ip_bytes = mp_obj_new_bytes(buf, len); +} + +mp_obj_t common_hal_ipaddress_ipv4address_get_packed(ipaddress_ipv4address_obj_t* self) { + return self->ip_bytes; +} diff --git a/shared-module/_pew/PewPew.h b/shared-module/ipaddress/IPv4Address.h index da1cae0a2..3f2bde091 100644 --- a/shared-module/_pew/PewPew.h +++ b/shared-module/ipaddress/IPv4Address.h @@ -3,7 +3,7 @@ * * The MIT License (MIT) * - * Copyright (c) 2019 Radomir Dopieralski + * Copyright (c) 2020 Scott Shawcroft for Adafruit Industries * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -24,25 +24,14 @@ * THE SOFTWARE. */ -#ifndef MICROPY_INCLUDED_PEW_PEWPEW_H -#define MICROPY_INCLUDED_PEW_PEWPEW_H +#ifndef MICROPY_INCLUDED_SHARED_MODULE_IPADDRESS_IPV4ADDRESS_H +#define MICROPY_INCLUDED_SHARED_MODULE_IPADDRESS_IPV4ADDRESS_H -#include <stdint.h> -#include "shared-bindings/digitalio/DigitalInOut.h" +#include "py/obj.h" typedef struct { mp_obj_base_t base; - uint8_t* buffer; - mp_obj_t* rows; - mp_obj_t* cols; - digitalio_digitalinout_obj_t *buttons; - uint8_t rows_size; - uint8_t cols_size; - uint8_t pressed; -} pew_obj_t; + mp_obj_t ip_bytes; +} ipaddress_ipv4address_obj_t; -void pew_init(void); -void pewpew_interrupt_handler(uint8_t index); -void pew_reset(void); - -#endif // MICROPY_INCLUDED_PEW_PEWPEW_H +#endif // MICROPY_INCLUDED_SHARED_MODULE_IPADDRESS_IPV4ADDRESS_H diff --git a/shared-module/ipaddress/__init__.c b/shared-module/ipaddress/__init__.c new file mode 100644 index 000000000..a8f8e1caf --- /dev/null +++ b/shared-module/ipaddress/__init__.c @@ -0,0 +1,35 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2020 Scott Shawcroft for Adafruit Industries + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "shared-bindings/ipaddress/__init__.h" +#include "shared-bindings/ipaddress/IPv4Address.h" + +mp_obj_t common_hal_ipaddress_new_ipv4address(uint32_t value) { + ipaddress_ipv4address_obj_t* self = m_new_obj(ipaddress_ipv4address_obj_t); + self->base.type = &ipaddress_ipv4address_type; + common_hal_ipaddress_ipv4address_construct(self, (uint8_t*) &value, 4); + return MP_OBJ_FROM_PTR(self); +} diff --git a/shared-module/ipaddress/__init__.h b/shared-module/ipaddress/__init__.h new file mode 100644 index 000000000..7d5f2c220 --- /dev/null +++ b/shared-module/ipaddress/__init__.h @@ -0,0 +1,36 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2020 Scott Shawcroft for Adafruit Industries + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifndef MICROPY_INCLUDED_SHARED_MODULE_IPADDRESS___INIT___H +#define MICROPY_INCLUDED_SHARED_MODULE_IPADDRESS___INIT___H + +#include <stdint.h> + +#include "py/obj.h" + +mp_obj_t common_hal_ipaddress_new_ipv4(uint32_t value); + +#endif // MICROPY_INCLUDED_SHARED_MODULE_IPADDRESS___INIT___H diff --git a/shared-module/memorymonitor/AllocationAlarm.c b/shared-module/memorymonitor/AllocationAlarm.c new file mode 100644 index 000000000..35f4e4c63 --- /dev/null +++ b/shared-module/memorymonitor/AllocationAlarm.c @@ -0,0 +1,94 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2020 Scott Shawcroft for Adafruit Industries + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "shared-bindings/memorymonitor/__init__.h" +#include "shared-bindings/memorymonitor/AllocationAlarm.h" + +#include "py/gc.h" +#include "py/mpstate.h" +#include "py/runtime.h" + +void common_hal_memorymonitor_allocationalarm_construct(memorymonitor_allocationalarm_obj_t* self, size_t minimum_block_count) { + self->minimum_block_count = minimum_block_count; + self->next = NULL; + self->previous = NULL; +} + +void common_hal_memorymonitor_allocationalarm_set_ignore(memorymonitor_allocationalarm_obj_t* self, mp_int_t count) { + self->count = count; +} + +void common_hal_memorymonitor_allocationalarm_pause(memorymonitor_allocationalarm_obj_t* self) { + // Check to make sure we aren't already paused. We can be if we're exiting from an exception we + // caused. + if (self->previous == NULL) { + return; + } + *self->previous = self->next; + self->next = NULL; + self->previous = NULL; +} + +void common_hal_memorymonitor_allocationalarm_resume(memorymonitor_allocationalarm_obj_t* self) { + if (self->previous != NULL) { + mp_raise_RuntimeError(translate("Already running")); + } + self->next = MP_STATE_VM(active_allocationalarms); + self->previous = (memorymonitor_allocationalarm_obj_t**) &MP_STATE_VM(active_allocationalarms); + if (self->next != NULL) { + self->next->previous = &self->next; + } + MP_STATE_VM(active_allocationalarms) = self; +} + +void memorymonitor_allocationalarms_allocation(size_t block_count) { + memorymonitor_allocationalarm_obj_t* alarm = MP_OBJ_TO_PTR(MP_STATE_VM(active_allocationalarms)); + size_t alert_count = 0; + while (alarm != NULL) { + // Hold onto next in case we remove the alarm from the list. + memorymonitor_allocationalarm_obj_t* next = alarm->next; + if (block_count >= alarm->minimum_block_count) { + if (alarm->count > 0) { + alarm->count--; + } else { + // Uncomment the breakpoint below if you want to use a C debugger to figure out the C + // call stack for an allocation. + // asm("bkpt"); + // Pause now because we may alert when throwing the exception too. + common_hal_memorymonitor_allocationalarm_pause(alarm); + alert_count++; + } + } + alarm = next; + } + if (alert_count > 0) { + mp_raise_memorymonitor_AllocationError(translate("Attempt to allocate %d blocks"), block_count); + } +} + +void memorymonitor_allocationalarms_reset(void) { + MP_STATE_VM(active_allocationalarms) = NULL; +} diff --git a/shared-module/memorymonitor/AllocationAlarm.h b/shared-module/memorymonitor/AllocationAlarm.h new file mode 100644 index 000000000..172c24f6c --- /dev/null +++ b/shared-module/memorymonitor/AllocationAlarm.h @@ -0,0 +1,51 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2020 Scott Shawcroft for Adafruit Industries + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifndef MICROPY_INCLUDED_SHARED_MODULE_MEMORYMONITOR_ALLOCATIONALARM_H +#define MICROPY_INCLUDED_SHARED_MODULE_MEMORYMONITOR_ALLOCATIONALARM_H + +#include <stdbool.h> +#include <stdint.h> + +#include "py/obj.h" + +typedef struct _memorymonitor_allocationalarm_obj_t memorymonitor_allocationalarm_obj_t; + +#define ALLOCATION_SIZE_BUCKETS 16 + +typedef struct _memorymonitor_allocationalarm_obj_t { + mp_obj_base_t base; + size_t minimum_block_count; + mp_int_t count; + // Store the location that points to us so we can remove ourselves. + memorymonitor_allocationalarm_obj_t** previous; + memorymonitor_allocationalarm_obj_t* next; +} memorymonitor_allocationalarm_obj_t; + +void memorymonitor_allocationalarms_allocation(size_t block_count); +void memorymonitor_allocationalarms_reset(void); + +#endif // MICROPY_INCLUDED_SHARED_MODULE_MEMORYMONITOR_ALLOCATIONALARM_H diff --git a/shared-module/memorymonitor/AllocationSize.c b/shared-module/memorymonitor/AllocationSize.c new file mode 100644 index 000000000..c28e65592 --- /dev/null +++ b/shared-module/memorymonitor/AllocationSize.c @@ -0,0 +1,91 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2020 Scott Shawcroft for Adafruit Industries + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "shared-bindings/memorymonitor/AllocationSize.h" + +#include "py/gc.h" +#include "py/mpstate.h" +#include "py/runtime.h" + +void common_hal_memorymonitor_allocationsize_construct(memorymonitor_allocationsize_obj_t* self) { + common_hal_memorymonitor_allocationsize_clear(self); + self->next = NULL; + self->previous = NULL; +} + +void common_hal_memorymonitor_allocationsize_pause(memorymonitor_allocationsize_obj_t* self) { + *self->previous = self->next; + self->next = NULL; + self->previous = NULL; +} + +void common_hal_memorymonitor_allocationsize_resume(memorymonitor_allocationsize_obj_t* self) { + if (self->previous != NULL) { + mp_raise_RuntimeError(translate("Already running")); + } + self->next = MP_STATE_VM(active_allocationsizes); + self->previous = (memorymonitor_allocationsize_obj_t**) &MP_STATE_VM(active_allocationsizes); + if (self->next != NULL) { + self->next->previous = &self->next; + } + MP_STATE_VM(active_allocationsizes) = self; +} + +void common_hal_memorymonitor_allocationsize_clear(memorymonitor_allocationsize_obj_t* self) { + for (size_t i = 0; i < ALLOCATION_SIZE_BUCKETS; i++) { + self->buckets[i] = 0; + } +} + +uint16_t common_hal_memorymonitor_allocationsize_get_len(memorymonitor_allocationsize_obj_t* self) { + return ALLOCATION_SIZE_BUCKETS; +} + +size_t common_hal_memorymonitor_allocationsize_get_bytes_per_block(memorymonitor_allocationsize_obj_t* self) { + return BYTES_PER_BLOCK; +} + +uint16_t common_hal_memorymonitor_allocationsize_get_item(memorymonitor_allocationsize_obj_t* self, int16_t index) { + return self->buckets[index]; +} + +void memorymonitor_allocationsizes_track_allocation(size_t block_count) { + memorymonitor_allocationsize_obj_t* as = MP_OBJ_TO_PTR(MP_STATE_VM(active_allocationsizes)); + size_t power_of_two = 0; + block_count >>= 1; + while (block_count != 0) { + power_of_two++; + block_count >>= 1; + } + while (as != NULL) { + as->buckets[power_of_two]++; + as = as->next; + } +} + +void memorymonitor_allocationsizes_reset(void) { + MP_STATE_VM(active_allocationsizes) = NULL; +} diff --git a/shared-module/memorymonitor/AllocationSize.h b/shared-module/memorymonitor/AllocationSize.h new file mode 100644 index 000000000..3baab2213 --- /dev/null +++ b/shared-module/memorymonitor/AllocationSize.h @@ -0,0 +1,51 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2020 Scott Shawcroft for Adafruit Industries + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifndef MICROPY_INCLUDED_SHARED_MODULE_MEMORYMONITOR_ALLOCATIONSIZE_H +#define MICROPY_INCLUDED_SHARED_MODULE_MEMORYMONITOR_ALLOCATIONSIZE_H + +#include <stdbool.h> +#include <stdint.h> + +#include "py/obj.h" + +typedef struct _memorymonitor_allocationsize_obj_t memorymonitor_allocationsize_obj_t; + +#define ALLOCATION_SIZE_BUCKETS 16 + +typedef struct _memorymonitor_allocationsize_obj_t { + mp_obj_base_t base; + uint16_t buckets[ALLOCATION_SIZE_BUCKETS]; + // Store the location that points to us so we can remove ourselves. + memorymonitor_allocationsize_obj_t** previous; + memorymonitor_allocationsize_obj_t* next; + bool paused; +} memorymonitor_allocationsize_obj_t; + +void memorymonitor_allocationsizes_track_allocation(size_t block_count); +void memorymonitor_allocationsizes_reset(void); + +#endif // MICROPY_INCLUDED_SHARED_MODULE_MEMORYMONITOR_ALLOCATIONSIZE_H diff --git a/shared-module/memorymonitor/__init__.c b/shared-module/memorymonitor/__init__.c new file mode 100644 index 000000000..6cb424153 --- /dev/null +++ b/shared-module/memorymonitor/__init__.c @@ -0,0 +1,39 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2020 Scott Shawcroft + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "shared-module/memorymonitor/__init__.h" +#include "shared-module/memorymonitor/AllocationAlarm.h" +#include "shared-module/memorymonitor/AllocationSize.h" + +void memorymonitor_track_allocation(size_t block_count) { + memorymonitor_allocationalarms_allocation(block_count); + memorymonitor_allocationsizes_track_allocation(block_count); +} + +void memorymonitor_reset(void) { + memorymonitor_allocationalarms_reset(); + memorymonitor_allocationsizes_reset(); +} diff --git a/shared-module/memorymonitor/__init__.h b/shared-module/memorymonitor/__init__.h new file mode 100644 index 000000000..f47f6434b --- /dev/null +++ b/shared-module/memorymonitor/__init__.h @@ -0,0 +1,35 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2016 Scott Shawcroft for Adafruit Industries + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION 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_MEMORYMONITOR___INIT___H +#define MICROPY_INCLUDED_MEMORYMONITOR___INIT___H + +#include <stddef.h> + +void memorymonitor_track_allocation(size_t block_count); +void memorymonitor_reset(void); + +#endif // MICROPY_INCLUDED_MEMORYMONITOR___INIT___H diff --git a/shared-module/network/__init__.c b/shared-module/network/__init__.c index 925e9a2a3..a45191d8c 100644 --- a/shared-module/network/__init__.c +++ b/shared-module/network/__init__.c @@ -37,7 +37,7 @@ #include "shared-module/network/__init__.h" -// mod_network_nic_list needs to be declared in mpconfigport.h +// mod_network_nic_list needs to be declared in mpconfigport.h void network_module_init(void) { @@ -47,7 +47,7 @@ void network_module_init(void) { void network_module_deinit(void) { for (mp_uint_t i = 0; i < MP_STATE_PORT(mod_network_nic_list).len; i++) { mp_obj_t nic = MP_STATE_PORT(mod_network_nic_list).items[i]; - mod_network_nic_type_t *nic_type = (mod_network_nic_type_t*)mp_obj_get_type(nic); + mod_network_nic_type_t *nic_type = (mod_network_nic_type_t*)mp_obj_get_type(nic); if (nic_type->deinit != NULL) nic_type->deinit(nic); } mp_obj_list_set_len(&MP_STATE_PORT(mod_network_nic_list), 0); @@ -61,7 +61,7 @@ void network_module_background(void) { for (mp_uint_t i = 0; i < MP_STATE_PORT(mod_network_nic_list).len; i++) { mp_obj_t nic = MP_STATE_PORT(mod_network_nic_list).items[i]; - mod_network_nic_type_t *nic_type = (mod_network_nic_type_t*)mp_obj_get_type(nic); + mod_network_nic_type_t *nic_type = (mod_network_nic_type_t*)mp_obj_get_type(nic); if (nic_type->timer_tick != NULL) nic_type->timer_tick(nic); } } diff --git a/shared-module/network/__init__.h b/shared-module/network/__init__.h index f4eb05bb5..280cfe6c8 100644 --- a/shared-module/network/__init__.h +++ b/shared-module/network/__init__.h @@ -3,7 +3,7 @@ * * The MIT License (MIT) * - * Copyright (c) 2013, 2014 Damien P. George + * SPDX-FileCopyrightText: Copyright (c) 2013, 2014 Damien P. George * Copyright (c) 2018 Nick Moore * * Permission is hereby granted, free of charge, to any person obtaining a copy diff --git a/shared-module/os/__init__.c b/shared-module/os/__init__.c index 8060eec4f..39cf40fda 100644 --- a/shared-module/os/__init__.c +++ b/shared-module/os/__init__.c @@ -3,7 +3,7 @@ * * The MIT License (MIT) * - * Copyright (c) 2013, 2014 Damien P. George + * SPDX-FileCopyrightText: Copyright (c) 2013, 2014 Damien P. George * Copyright (c) 2015 Josef Gajdusek * Copyright (c) 2017 Scott Shawcroft for Adafruit Industries * diff --git a/shared-module/rgbmatrix/RGBMatrix.c b/shared-module/rgbmatrix/RGBMatrix.c index df064ff81..94c3eda27 100644 --- a/shared-module/rgbmatrix/RGBMatrix.c +++ b/shared-module/rgbmatrix/RGBMatrix.c @@ -66,9 +66,9 @@ void common_hal_rgbmatrix_rgbmatrix_construct(rgbmatrix_rgbmatrix_obj_t *self, i } void common_hal_rgbmatrix_rgbmatrix_reconstruct(rgbmatrix_rgbmatrix_obj_t* self, mp_obj_t framebuffer) { + common_hal_rgbmatrix_timer_disable(self->timer); if (framebuffer) { self->framebuffer = framebuffer; - framebuffer = mp_obj_new_bytearray_of_zeros(self->bufsize); mp_get_buffer_raise(self->framebuffer, &self->bufinfo, MP_BUFFER_READ); if (mp_get_buffer(self->framebuffer, &self->bufinfo, MP_BUFFER_RW)) { self->bufinfo.typecode = 'H' | MP_OBJ_ARRAY_TYPECODE_FLAG_RW; @@ -78,18 +78,19 @@ void common_hal_rgbmatrix_rgbmatrix_reconstruct(rgbmatrix_rgbmatrix_obj_t* self, // verify that the matrix is big enough mp_get_index(mp_obj_get_type(self->framebuffer), self->bufinfo.len, MP_OBJ_NEW_SMALL_INT(self->bufsize-1), false); } else { - _PM_FREE(self->bufinfo.buf); - _PM_FREE(self->core.rgbPins); - _PM_FREE(self->core.addr); - _PM_FREE(self->core.screenData); + _PM_free(self->bufinfo.buf); + _PM_free(self->protomatter.rgbPins); + _PM_free(self->protomatter.addr); + _PM_free(self->protomatter.screenData); self->framebuffer = NULL; - self->bufinfo.buf = _PM_allocator_impl(self->bufsize); + self->bufinfo.buf = common_hal_rgbmatrix_allocator_impl(self->bufsize); self->bufinfo.len = self->bufsize; self->bufinfo.typecode = 'H' | MP_OBJ_ARRAY_TYPECODE_FLAG_RW; } - ProtomatterStatus stat = _PM_init(&self->core, + memset(&self->protomatter, 0, sizeof(self->protomatter)); + ProtomatterStatus stat = _PM_init(&self->protomatter, self->width, self->bit_depth, self->rgb_count/6, self->rgb_pins, self->addr_count, self->addr_pins, @@ -97,18 +98,21 @@ void common_hal_rgbmatrix_rgbmatrix_reconstruct(rgbmatrix_rgbmatrix_obj_t* self, self->doublebuffer, self->timer); if (stat == PROTOMATTER_OK) { - _PM_protoPtr = &self->core; + _PM_protoPtr = &self->protomatter; common_hal_mcu_disable_interrupts(); common_hal_rgbmatrix_timer_enable(self->timer); - stat = _PM_begin(&self->core); - _PM_convert_565(&self->core, self->bufinfo.buf, self->width); + stat = _PM_begin(&self->protomatter); + + if (stat == PROTOMATTER_OK) { + _PM_convert_565(&self->protomatter, self->bufinfo.buf, self->width); + } common_hal_mcu_enable_interrupts(); - _PM_swapbuffer_maybe(&self->core); + if (stat == PROTOMATTER_OK) { + _PM_swapbuffer_maybe(&self->protomatter); + } } if (stat != PROTOMATTER_OK) { - // XXX this deinit() actually makes crashy-crashy - // can trigger it by sending inappropriate pins common_hal_rgbmatrix_rgbmatrix_deinit(self); switch (stat) { case PROTOMATTER_ERR_PINS: @@ -117,7 +121,9 @@ void common_hal_rgbmatrix_rgbmatrix_reconstruct(rgbmatrix_rgbmatrix_obj_t* self, case PROTOMATTER_ERR_ARG: mp_raise_ValueError(translate("Invalid argument")); break; - case PROTOMATTER_ERR_MALLOC: /// should have already been signaled as NLR + case PROTOMATTER_ERR_MALLOC: + mp_raise_msg(&mp_type_MemoryError, NULL); + break; default: mp_raise_msg_varg(&mp_type_RuntimeError, translate("Internal error #%d"), (int)stat); @@ -126,7 +132,6 @@ void common_hal_rgbmatrix_rgbmatrix_reconstruct(rgbmatrix_rgbmatrix_obj_t* self, } self->paused = 0; - } STATIC void free_pin(uint8_t *pin) { @@ -148,7 +153,7 @@ void common_hal_rgbmatrix_rgbmatrix_deinit(rgbmatrix_rgbmatrix_obj_t* self) { self->timer = 0; } - if (_PM_protoPtr == &self->core) { + if (_PM_protoPtr == &self->protomatter) { _PM_protoPtr = NULL; } @@ -158,14 +163,14 @@ void common_hal_rgbmatrix_rgbmatrix_deinit(rgbmatrix_rgbmatrix_obj_t* self) { free_pin(&self->latch_pin); free_pin(&self->oe_pin); - if (self->core.rgbPins) { - _PM_free(&self->core); + if (self->protomatter.rgbPins) { + _PM_deallocate(&self->protomatter); } - memset(&self->core, 0, sizeof(self->core)); + memset(&self->protomatter, 0, sizeof(self->protomatter)); // If it was supervisor-allocated, it is supervisor-freed and the pointer // is zeroed, otherwise the pointer is just zeroed - _PM_FREE(self->bufinfo.buf); + _PM_free(self->bufinfo.buf); self->base.type = NULL; // If a framebuffer was passed in to the constructor, NULL the reference @@ -175,16 +180,18 @@ void common_hal_rgbmatrix_rgbmatrix_deinit(rgbmatrix_rgbmatrix_obj_t* self) { void rgbmatrix_rgbmatrix_collect_ptrs(rgbmatrix_rgbmatrix_obj_t* self) { gc_collect_ptr(self->framebuffer); - gc_collect_ptr(self->core.rgbPins); - gc_collect_ptr(self->core.addr); - gc_collect_ptr(self->core.screenData); + gc_collect_ptr(self->protomatter.rgbPins); + gc_collect_ptr(self->protomatter.addr); + gc_collect_ptr(self->protomatter.screenData); } void common_hal_rgbmatrix_rgbmatrix_set_paused(rgbmatrix_rgbmatrix_obj_t* self, bool paused) { if (paused && !self->paused) { - _PM_stop(&self->core); + _PM_stop(&self->protomatter); } else if (!paused && self->paused) { - _PM_resume(&self->core); + _PM_resume(&self->protomatter); + _PM_convert_565(&self->protomatter, self->bufinfo.buf, self->width); + _PM_swapbuffer_maybe(&self->protomatter); } self->paused = paused; } @@ -194,8 +201,10 @@ bool common_hal_rgbmatrix_rgbmatrix_get_paused(rgbmatrix_rgbmatrix_obj_t* self) } void common_hal_rgbmatrix_rgbmatrix_refresh(rgbmatrix_rgbmatrix_obj_t* self) { - _PM_convert_565(&self->core, self->bufinfo.buf, self->width); - _PM_swapbuffer_maybe(&self->core); + if (!self->paused) { + _PM_convert_565(&self->protomatter, self->bufinfo.buf, self->width); + _PM_swapbuffer_maybe(&self->protomatter); + } } int common_hal_rgbmatrix_rgbmatrix_get_width(rgbmatrix_rgbmatrix_obj_t* self) { @@ -207,3 +216,19 @@ int common_hal_rgbmatrix_rgbmatrix_get_height(rgbmatrix_rgbmatrix_obj_t* self) { return computed_height; } +void *common_hal_rgbmatrix_allocator_impl(size_t sz) { + if (gc_alloc_possible()) { + return m_malloc_maybe(sz + sizeof(void*), true); + } else { + supervisor_allocation *allocation = allocate_memory(align32_size(sz), false); + return allocation ? allocation->ptr : NULL; + } +} + +void common_hal_rgbmatrix_free_impl(void *ptr_in) { + supervisor_allocation *allocation = allocation_from_ptr(ptr_in); + + if (allocation) { + free_memory(allocation); + } +} diff --git a/shared-module/rgbmatrix/RGBMatrix.h b/shared-module/rgbmatrix/RGBMatrix.h index e69de29bb..6dbc6fd41 100644 --- a/shared-module/rgbmatrix/RGBMatrix.h +++ b/shared-module/rgbmatrix/RGBMatrix.h @@ -0,0 +1,47 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2020 Jeff Epler for Adafruit Industries + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#pragma once + +#include "lib/protomatter/src/core.h" + +extern const mp_obj_type_t rgbmatrix_RGBMatrix_type; +typedef struct { + mp_obj_base_t base; + mp_obj_t framebuffer; + mp_buffer_info_t bufinfo; + Protomatter_core protomatter; + void *timer; + uint16_t bufsize, width; + uint8_t rgb_pins[30]; + uint8_t addr_pins[10]; + uint8_t clock_pin, latch_pin, oe_pin; + uint8_t rgb_count, addr_count; + uint8_t bit_depth; + bool core_is_initialized; + bool paused; + bool doublebuffer; +} rgbmatrix_rgbmatrix_obj_t; diff --git a/shared-module/rgbmatrix/allocator.h b/shared-module/rgbmatrix/allocator.h index 5e6f0b41d..3431046d5 100644 --- a/shared-module/rgbmatrix/allocator.h +++ b/shared-module/rgbmatrix/allocator.h @@ -1,29 +1,37 @@ -#ifndef MICROPY_INCLUDED_SHARED_MODULE_RGBMATRIX_ALLOCATOR_H -#define MICROPY_INCLUDED_SHARED_MODULE_RGBMATRIX_ALLOCATOR_H +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2020 Jeff Epler for Adafruit Industries + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#pragma once #include <stdbool.h> #include "py/gc.h" #include "py/misc.h" #include "supervisor/memory.h" -#define _PM_ALLOCATOR _PM_allocator_impl -#define _PM_FREE(x) (_PM_free_impl((x)), (x)=NULL, (void)0) - -static inline void *_PM_allocator_impl(size_t sz) { - if (gc_alloc_possible()) { - return m_malloc(sz + sizeof(void*), true); - } else { - supervisor_allocation *allocation = allocate_memory(align32_size(sz), false); - return allocation ? allocation->ptr : NULL; - } -} - -static inline void _PM_free_impl(void *ptr_in) { - supervisor_allocation *allocation = allocation_from_ptr(ptr_in); - - if (allocation) { - free_memory(allocation); - } -} - -#endif +#define _PM_allocate common_hal_rgbmatrix_allocator_impl +#define _PM_free(x) (common_hal_rgbmatrix_free_impl((x)), (x)=NULL, (void)0) +extern void *common_hal_rgbmatrix_allocator_impl(size_t sz); +extern void common_hal_rgbmatrix_free_impl(void *); diff --git a/shared-module/sdcardio/SDCard.c b/shared-module/sdcardio/SDCard.c new file mode 100644 index 000000000..9e861279d --- /dev/null +++ b/shared-module/sdcardio/SDCard.c @@ -0,0 +1,466 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2020 Jeff Epler for Adafruit Industries + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +// This implementation largely follows the structure of adafruit_sdcard.py + +#include "shared-bindings/busio/SPI.h" +#include "shared-bindings/digitalio/DigitalInOut.h" +#include "shared-bindings/time/__init__.h" +#include "shared-bindings/util.h" +#include "shared-module/sdcardio/SDCard.h" + +#include "py/mperrno.h" + +#if 0 +#define DEBUG_PRINT(...) ((void)mp_printf(&mp_plat_print, ## __VA_ARGS__)) +#else +#define DEBUG_PRINT(...) ((void)0) +#endif + +#define CMD_TIMEOUT (200) + +#define R1_IDLE_STATE (1<<0) +#define R1_ILLEGAL_COMMAND (1<<2) + +#define TOKEN_CMD25 (0xFC) +#define TOKEN_STOP_TRAN (0xFD) +#define TOKEN_DATA (0xFE) + +STATIC bool lock_and_configure_bus(sdcardio_sdcard_obj_t *self) { + if (!common_hal_busio_spi_try_lock(self->bus)) { + return false; + } + common_hal_busio_spi_configure(self->bus, self->baudrate, 0, 0, 8); + common_hal_digitalio_digitalinout_set_value(&self->cs, false); + return true; +} + +STATIC void lock_bus_or_throw(sdcardio_sdcard_obj_t *self) { + if (!lock_and_configure_bus(self)) { + mp_raise_OSError(EAGAIN); + } +} + +STATIC void clock_card(sdcardio_sdcard_obj_t *self, int bytes) { + uint8_t buf[] = {0xff}; + common_hal_digitalio_digitalinout_set_value(&self->cs, true); + for (int i=0; i<bytes; i++) { + common_hal_busio_spi_write(self->bus, buf, 1); + } +} + +STATIC void extraclock_and_unlock_bus(sdcardio_sdcard_obj_t *self) { + clock_card(self, 1); + common_hal_busio_spi_unlock(self->bus); +} + +static uint8_t CRC7(const uint8_t* data, uint8_t n) { + uint8_t crc = 0; + for (uint8_t i = 0; i < n; i++) { + uint8_t d = data[i]; + for (uint8_t j = 0; j < 8; j++) { + crc <<= 1; + if ((d & 0x80) ^ (crc & 0x80)) { + crc ^= 0x09; + } + d <<= 1; + } + } + return (crc << 1) | 1; +} + +#define READY_TIMEOUT_NS (300 * 1000 * 1000) // 300ms +STATIC void wait_for_ready(sdcardio_sdcard_obj_t *self) { + uint64_t deadline = common_hal_time_monotonic_ns() + READY_TIMEOUT_NS; + while (common_hal_time_monotonic_ns() < deadline) { + uint8_t b; + common_hal_busio_spi_read(self->bus, &b, 1, 0xff); + if (b == 0xff) { + break; + } + } +} + +// In Python API, defaults are response=None, data_block=True, wait=True +STATIC int cmd(sdcardio_sdcard_obj_t *self, int cmd, int arg, void *response_buf, size_t response_len, bool data_block, bool wait) { + DEBUG_PRINT("cmd % 3d [%02x] arg=% 11d [%08x] len=%d%s%s\n", cmd, cmd, arg, arg, response_len, data_block ? " data" : "", wait ? " wait" : ""); + uint8_t cmdbuf[6]; + cmdbuf[0] = cmd | 0x40; + cmdbuf[1] = (arg >> 24) & 0xff; + cmdbuf[2] = (arg >> 16) & 0xff; + cmdbuf[3] = (arg >> 8) & 0xff; + cmdbuf[4] = arg & 0xff; + cmdbuf[5] = CRC7(cmdbuf, 5); + + if (wait) { + wait_for_ready(self); + } + + common_hal_busio_spi_write(self->bus, cmdbuf, sizeof(cmdbuf)); + + // Wait for the response (response[7] == 0) + bool response_received = false; + for (int i=0; i<CMD_TIMEOUT; i++) { + common_hal_busio_spi_read(self->bus, cmdbuf, 1, 0xff); + if ((cmdbuf[0] & 0x80) == 0) { + response_received = true; + break; + } + } + + if (!response_received) { + return -EIO; + } + + if (response_buf) { + + if (data_block) { + cmdbuf[1] = 0xff; + do { + // Wait for the start block byte + common_hal_busio_spi_read(self->bus, cmdbuf+1, 1, 0xff); + } while (cmdbuf[1] != 0xfe); + } + + common_hal_busio_spi_read(self->bus, response_buf, response_len, 0xff); + + if (data_block) { + // Read and discard the CRC-CCITT checksum + common_hal_busio_spi_read(self->bus, cmdbuf+1, 2, 0xff); + } + + } + + return cmdbuf[0]; +} + +STATIC int block_cmd(sdcardio_sdcard_obj_t *self, int cmd_, int block, void *response_buf, size_t response_len, bool data_block, bool wait) { + return cmd(self, cmd_, block * self->cdv, response_buf, response_len, true, true); +} + +STATIC bool cmd_nodata(sdcardio_sdcard_obj_t* self, int cmd, int response) { + uint8_t cmdbuf[2] = {cmd, 0xff}; + + common_hal_busio_spi_write(self->bus, cmdbuf, sizeof(cmdbuf)); + + // Wait for the response (response[7] == response) + for (int i=0; i<CMD_TIMEOUT; i++) { + common_hal_busio_spi_read(self->bus, cmdbuf, 1, 0xff); + if (cmdbuf[0] == response) { + return 0; + } + } + return -EIO; +} + +STATIC const compressed_string_t *init_card_v1(sdcardio_sdcard_obj_t *self) { + for (int i=0; i<CMD_TIMEOUT; i++) { + if (cmd(self, 41, 0, NULL, 0, true, true) == 0) { + return NULL; + } + } + return translate("timeout waiting for v1 card"); +} + +STATIC const compressed_string_t *init_card_v2(sdcardio_sdcard_obj_t *self) { + for (int i=0; i<CMD_TIMEOUT; i++) { + uint8_t ocr[4]; + common_hal_time_delay_ms(50); + cmd(self, 58, 0, ocr, sizeof(ocr), false, true); + cmd(self, 55, 0, NULL, 0, true, true); + if (cmd(self, 41, 0x40000000, NULL, 0, true, true) == 0) { + cmd(self, 58, 0, ocr, sizeof(ocr), false, true); + if ((ocr[0] & 0x40) != 0) { + self->cdv = 1; + } + return NULL; + } + } + return translate("timeout waiting for v2 card"); +} + +STATIC const compressed_string_t *init_card(sdcardio_sdcard_obj_t *self) { + clock_card(self, 10); + + common_hal_digitalio_digitalinout_set_value(&self->cs, false); + + // CMD0: init card: should return _R1_IDLE_STATE (allow 5 attempts) + { + bool reached_idle_state = false; + for (int i=0; i<5; i++) { + if (cmd(self, 0, 0, NULL, 0, true, true) == R1_IDLE_STATE) { + reached_idle_state = true; + break; + } + } + if (!reached_idle_state) { + return translate("no SD card"); + } + } + + // CMD8: determine card version + { + uint8_t rb7[4]; + int response = cmd(self, 8, 0x1AA, rb7, sizeof(rb7), false, true); + if (response == R1_IDLE_STATE) { + const compressed_string_t *result =init_card_v2(self); + if (result != NULL) { + return result; + } + } else if (response == (R1_IDLE_STATE | R1_ILLEGAL_COMMAND)) { + const compressed_string_t *result =init_card_v1(self); + if (result != NULL) { + return result; + } + } else { + return translate("couldn't determine SD card version"); + } + } + + // CMD9: get number of sectors + { + uint8_t csd[16]; + int response = cmd(self, 9, 0, csd, sizeof(csd), true, true); + if (response != 0) { + return translate("no response from SD card"); + } + int csd_version = (csd[0] & 0xC0) >> 6; + if (csd_version >= 2) { + return translate("SD card CSD format not supported"); + } + + if (csd_version == 1) { + self->sectors = ((csd[8] << 8 | csd[9]) + 1) * 1024; + } else { + uint32_t block_length = 1 << (csd[5] & 0xF); + uint32_t c_size = ((csd[6] & 0x3) << 10) | (csd[7] << 2) | ((csd[8] & 0xC) >> 6); + uint32_t mult = 1 << (((csd[9] & 0x3) << 1 | (csd[10] & 0x80) >> 7) + 2); + self->sectors = block_length / 512 * mult * (c_size + 1); + } + } + + // CMD16: set block length to 512 bytes + { + int response = cmd(self, 16, 512, NULL, 0, true, true); + if (response != 0) { + return translate("can't set 512 block size"); + } + } + + return NULL; +} + +void common_hal_sdcardio_sdcard_construct(sdcardio_sdcard_obj_t *self, busio_spi_obj_t *bus, mcu_pin_obj_t *cs, int baudrate) { + self->bus = bus; + common_hal_digitalio_digitalinout_construct(&self->cs, cs); + common_hal_digitalio_digitalinout_switch_to_output(&self->cs, true, DRIVE_MODE_PUSH_PULL); + + self->cdv = 512; + self->sectors = 0; + self->baudrate = 250000; + + lock_bus_or_throw(self); + const compressed_string_t *result = init_card(self); + extraclock_and_unlock_bus(self); + + if (result != NULL) { + common_hal_digitalio_digitalinout_deinit(&self->cs); + mp_raise_OSError_msg(result); + } + + self->baudrate = baudrate; +} + +void common_hal_sdcardio_sdcard_deinit(sdcardio_sdcard_obj_t *self) { + if (!self->bus) { + return; + } + self->bus = 0; + common_hal_digitalio_digitalinout_deinit(&self->cs); +} + +void common_hal_sdcardio_check_for_deinit(sdcardio_sdcard_obj_t *self) { + if (!self->bus) { + raise_deinited_error(); + } +} + +int common_hal_sdcardio_sdcard_get_blockcount(sdcardio_sdcard_obj_t *self) { + common_hal_sdcardio_check_for_deinit(self); + return self->sectors; +} + +int readinto(sdcardio_sdcard_obj_t *self, void *buf, size_t size) { + uint8_t aux[2] = {0, 0}; + while (aux[0] != 0xfe) { + common_hal_busio_spi_read(self->bus, aux, 1, 0xff); + } + + common_hal_busio_spi_read(self->bus, buf, size, 0xff); + + // Read checksum and throw it away + common_hal_busio_spi_read(self->bus, aux, sizeof(aux), 0xff); + return 0; +} + +int readblocks(sdcardio_sdcard_obj_t *self, uint32_t start_block, mp_buffer_info_t *buf) { + uint32_t nblocks = buf->len / 512; + if (nblocks == 1) { + // Use CMD17 to read a single block + return block_cmd(self, 17, start_block, buf->buf, buf->len, true, true); + } else { + // Use CMD18 to read multiple blocks + int r = block_cmd(self, 18, start_block, NULL, 0, true, true); + if (r < 0) { + return r; + } + + uint8_t *ptr = buf->buf; + while (nblocks--) { + r = readinto(self, ptr, 512); + if (r < 0) { + return r; + } + ptr += 512; + } + + // End the multi-block read + r = cmd(self, 12, 0, NULL, 0, true, false); + + // Return first status 0 or last before card ready (0xff) + while (r != 0) { + uint8_t single_byte; + common_hal_busio_spi_read(self->bus, &single_byte, 1, 0xff); + if (single_byte & 0x80) { + return r; + } + r = single_byte; + } + } + return 0; +} + +int common_hal_sdcardio_sdcard_readblocks(sdcardio_sdcard_obj_t *self, uint32_t start_block, mp_buffer_info_t *buf) { + common_hal_sdcardio_check_for_deinit(self); + if (buf->len % 512 != 0) { + mp_raise_ValueError(translate("Buffer length must be a multiple of 512")); + } + + lock_and_configure_bus(self); + int r = readblocks(self, start_block, buf); + extraclock_and_unlock_bus(self); + return r; +} + +int _write(sdcardio_sdcard_obj_t *self, uint8_t token, void *buf, size_t size) { + wait_for_ready(self); + + uint8_t cmd[2]; + cmd[0] = token; + + common_hal_busio_spi_write(self->bus, cmd, 1); + common_hal_busio_spi_write(self->bus, buf, size); + + cmd[0] = cmd[1] = 0xff; + common_hal_busio_spi_write(self->bus, cmd, 2); + + // Check the response + // This differs from the traditional adafruit_sdcard handling, + // but adafruit_sdcard also ignored the return value of SDCard._write(!) + // so nobody noticed + // + // + // Response is as follows: + // x x x 0 STAT 1 + // 7 6 5 4 3..1 0 + // with STATUS 010 indicating "data accepted", and other status bit + // combinations indicating failure. + // In practice, I was seeing cmd[0] as 0xe5, indicating success + for (int i=0; i<CMD_TIMEOUT; i++) { + common_hal_busio_spi_read(self->bus, cmd, 1, 0xff); + DEBUG_PRINT("i=%02d cmd[0] = 0x%02x\n", i, cmd[0]); + if ((cmd[0] & 0b00010001) == 0b00000001) { + if ((cmd[0] & 0x1f) != 0x5) { + return -EIO; + } else { + break; + } + } + } + + // Wait for the write to finish + do { + common_hal_busio_spi_read(self->bus, cmd, 1, 0xff); + } while (cmd[0] == 0); + + // Success + return 0; +} + +int writeblocks(sdcardio_sdcard_obj_t *self, uint32_t start_block, mp_buffer_info_t *buf) { + common_hal_sdcardio_check_for_deinit(self); + uint32_t nblocks = buf->len / 512; + if (nblocks == 1) { + // Use CMD24 to write a single block + int r = block_cmd(self, 24, start_block, NULL, 0, true, true); + if (r < 0) { + return r; + } + r = _write(self, TOKEN_DATA, buf->buf, buf->len); + if (r < 0) { + return r; + } + } else { + // Use CMD25 to write multiple block + int r = block_cmd(self, 25, start_block, NULL, 0, true, true); + if (r < 0) { + return r; + } + + uint8_t *ptr = buf->buf; + while (nblocks--) { + r = _write(self, TOKEN_CMD25, ptr, 512); + if (r < 0) { + return r; + } + ptr += 512; + } + + cmd_nodata(self, TOKEN_STOP_TRAN, 0); + } + return 0; +} + +int common_hal_sdcardio_sdcard_writeblocks(sdcardio_sdcard_obj_t *self, uint32_t start_block, mp_buffer_info_t *buf) { + common_hal_sdcardio_check_for_deinit(self); + if (buf->len % 512 != 0) { + mp_raise_ValueError(translate("Buffer length must be a multiple of 512")); + } + lock_and_configure_bus(self); + int r = writeblocks(self, start_block, buf); + extraclock_and_unlock_bus(self); + return r; +} diff --git a/shared-module/sdcardio/SDCard.h b/shared-module/sdcardio/SDCard.h new file mode 100644 index 000000000..76c906029 --- /dev/null +++ b/shared-module/sdcardio/SDCard.h @@ -0,0 +1,51 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2020 Jeff Epler for Adafruit Industries + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#pragma once + +#include "py/obj.h" +#include "py/objproperty.h" +#include "py/runtime.h" +#include "py/objarray.h" + +#include "common-hal/busio/SPI.h" +#include "common-hal/digitalio/DigitalInOut.h" + +typedef struct { + mp_obj_base_t base; + busio_spi_obj_t *bus; + digitalio_digitalinout_obj_t cs; + int cdv; + int baudrate; + uint32_t sectors; +} sdcardio_sdcard_obj_t; + +void common_hal_sdcardio_sdcard_construct(sdcardio_sdcard_obj_t *self, busio_spi_obj_t *spi, mcu_pin_obj_t *cs, int baudrate); +void common_hal_sdcardio_sdcard_deinit(sdcardio_sdcard_obj_t *self); +void common_hal_sdcardio_sdcard_check_for_deinit(sdcardio_sdcard_obj_t *self); +int common_hal_sdcardio_sdcard_get_blockcount(sdcardio_sdcard_obj_t *self); +int common_hal_sdcardio_sdcard_readblocks(sdcardio_sdcard_obj_t *self, uint32_t start_block, mp_buffer_info_t *buf); +int common_hal_sdcardio_sdcard_writeblocks(sdcardio_sdcard_obj_t *self, uint32_t start_block, mp_buffer_info_t *buf); diff --git a/shared-module/sdcardio/__init__.c b/shared-module/sdcardio/__init__.c new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/shared-module/sdcardio/__init__.c diff --git a/shared-module/sdcardio/__init__.h b/shared-module/sdcardio/__init__.h new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/shared-module/sdcardio/__init__.h diff --git a/shared-module/sharpdisplay/SharpMemoryFramebuffer.c b/shared-module/sharpdisplay/SharpMemoryFramebuffer.c new file mode 100644 index 000000000..c23446828 --- /dev/null +++ b/shared-module/sharpdisplay/SharpMemoryFramebuffer.c @@ -0,0 +1,275 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2020 Jeff Epler for Adafruit Industries + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include <string.h> + +#include "py/gc.h" + +#include "shared-bindings/board/__init__.h" +#include "shared-bindings/microcontroller/Pin.h" +#include "shared-bindings/sharpdisplay/SharpMemoryFramebuffer.h" +#include "shared-module/sharpdisplay/SharpMemoryFramebuffer.h" + +#include "supervisor/memory.h" + +#define SHARPMEM_BIT_WRITECMD_LSB (0x80) +#define SHARPMEM_BIT_VCOM_LSB (0x40) + +static inline void *hybrid_alloc(size_t sz) { + if (gc_alloc_possible()) { + return m_malloc(sz + sizeof(void*), true); + } else { + supervisor_allocation *allocation = allocate_memory(align32_size(sz), false); + if (!allocation) { + return NULL; + } + memset(allocation->ptr, 0, sz); + return allocation->ptr; + } +} + +static inline void hybrid_free(void *ptr_in) { + supervisor_allocation *allocation = allocation_from_ptr(ptr_in); + + if (allocation) { + free_memory(allocation); + } +} + +STATIC uint8_t bitrev(uint8_t n) { + uint8_t r = 0; + for(int i=0;i<8;i++) r |= ((n>>i) & 1)<<(7-i); + return r; +} + +int common_hal_sharpdisplay_framebuffer_get_width(sharpdisplay_framebuffer_obj_t *self) { + return self->width; +} + +int common_hal_sharpdisplay_framebuffer_get_height(sharpdisplay_framebuffer_obj_t *self) { + return self->height; +} + +int common_hal_sharpdisplay_framebuffer_get_row_stride(sharpdisplay_framebuffer_obj_t *self) { + return (self->width + 7) / 8 + 2; +} + +int common_hal_sharpdisplay_framebuffer_get_first_pixel_offset(sharpdisplay_framebuffer_obj_t *self) { + return 2; +} + +bool common_hal_sharpdisplay_framebuffer_get_reverse_pixels_in_byte(sharpdisplay_framebuffer_obj_t *self) { + return true; +} + +bool common_hal_sharpdisplay_framebuffer_get_pixels_in_byte_share_row(sharpdisplay_framebuffer_obj_t *self) { + return true; +} + +void common_hal_sharpdisplay_framebuffer_reset(sharpdisplay_framebuffer_obj_t *self) { + if (self->bus != &self->inline_bus +#if BOARD_SPI + && self->bus != common_hal_board_get_spi() +#endif + ) { + memcpy(&self->inline_bus, self->bus, sizeof(busio_spi_obj_t)); + self->bus = &self->inline_bus; + } +} + +void common_hal_sharpdisplay_framebuffer_reconstruct(sharpdisplay_framebuffer_obj_t *self) { + if (!allocation_from_ptr(self->bufinfo.buf)) { + self->bufinfo.buf = NULL; + } +} + +void common_hal_sharpdisplay_framebuffer_get_bufinfo(sharpdisplay_framebuffer_obj_t *self, mp_buffer_info_t *bufinfo) { + if (!self->bufinfo.buf) { + int row_stride = common_hal_sharpdisplay_framebuffer_get_row_stride(self); + int height = common_hal_sharpdisplay_framebuffer_get_height(self); + self->bufinfo.len = row_stride * height + 2; + self->bufinfo.buf = hybrid_alloc(self->bufinfo.len); + + uint8_t *data = self->bufinfo.buf; + *data++ = SHARPMEM_BIT_WRITECMD_LSB; + + for(int y=0; y<height; y++) { + *data = bitrev(y+1); + data += row_stride; + } + self->full_refresh = true; + } + *bufinfo = self->bufinfo; +} + +void common_hal_sharpdisplay_framebuffer_deinit(sharpdisplay_framebuffer_obj_t *self) { + if (self->base.type != &sharpdisplay_framebuffer_type) { + return; + } + + if (self->bus == &self->inline_bus) { + common_hal_busio_spi_deinit(self->bus); + } + + common_hal_reset_pin(self->chip_select.pin); + + hybrid_free(self->bufinfo.buf); + + memset(self, 0, sizeof(*self)); +} + +void common_hal_sharpdisplay_framebuffer_construct(sharpdisplay_framebuffer_obj_t *self, busio_spi_obj_t *spi, mcu_pin_obj_t *chip_select, int baudrate, int width, int height) { + common_hal_digitalio_digitalinout_construct(&self->chip_select, chip_select); + common_hal_digitalio_digitalinout_switch_to_output(&self->chip_select, true, DRIVE_MODE_PUSH_PULL); + common_hal_never_reset_pin(chip_select); + + self->bus = spi; + common_hal_busio_spi_never_reset(self->bus); + + self->width = width; + self->height = height; + self->baudrate = baudrate; + + int row_stride = common_hal_sharpdisplay_framebuffer_get_row_stride(self); + self->bufinfo.len = row_stride * height + 2; + self->bufinfo.buf = gc_alloc(self->bufinfo.len, false, true); + + uint8_t *data = self->bufinfo.buf; + *data++ = SHARPMEM_BIT_WRITECMD_LSB; + + for(int y=0; y<self->height; y++) { + *data = bitrev(y+1); + data += row_stride; + } + self->full_refresh = true; +} + +void common_hal_sharpdisplay_framebuffer_swapbuffers(sharpdisplay_framebuffer_obj_t *self, uint8_t *dirty_row_bitmask) { + // claim SPI bus + if (!common_hal_busio_spi_try_lock(self->bus)) { + return; + } + common_hal_busio_spi_configure(self->bus, self->baudrate, 0, 0, 8); + + // set chip select high + common_hal_digitalio_digitalinout_set_value(&self->chip_select, true); + + // output the toggling signal + uint8_t *data = self->bufinfo.buf; + data[0] ^= SHARPMEM_BIT_VCOM_LSB; + + common_hal_busio_spi_write(self->bus, data++, 1); + + // output each changed row + size_t row_stride = common_hal_sharpdisplay_framebuffer_get_row_stride(self); + for(int y=0; y<self->height; y++) { + if(self->full_refresh || (dirty_row_bitmask[y/8] & (1 << (y & 7)))) { + common_hal_busio_spi_write(self->bus, data, row_stride); + } + data += row_stride; + } + + // output a trailing zero + common_hal_busio_spi_write(self->bus, data, 1); + + // set chip select low + common_hal_digitalio_digitalinout_set_value(&self->chip_select, false); + + // release SPI bus + common_hal_busio_spi_unlock(self->bus); + + self->full_refresh = false; +} + +STATIC void sharpdisplay_framebuffer_deinit(mp_obj_t self_in) { + sharpdisplay_framebuffer_obj_t *self = self_in; + common_hal_sharpdisplay_framebuffer_deinit(self); +} + +STATIC void sharpdisplay_framebuffer_get_bufinfo(mp_obj_t self_in, mp_buffer_info_t *bufinfo) { + sharpdisplay_framebuffer_obj_t *self = self_in; + common_hal_sharpdisplay_framebuffer_get_bufinfo(self, bufinfo); +} + +STATIC int sharpdisplay_framebuffer_get_color_depth(mp_obj_t self_in) { + return 1; +} + +STATIC int sharpdisplay_framebuffer_get_height(mp_obj_t self_in) { + sharpdisplay_framebuffer_obj_t *self = self_in; + return common_hal_sharpdisplay_framebuffer_get_height(self); +} + +STATIC int sharpdisplay_framebuffer_get_width(mp_obj_t self_in) { + sharpdisplay_framebuffer_obj_t *self = self_in; + return common_hal_sharpdisplay_framebuffer_get_width(self); +} + +STATIC int sharpdisplay_framebuffer_get_first_pixel_offset(mp_obj_t self_in) { + sharpdisplay_framebuffer_obj_t *self = self_in; + return common_hal_sharpdisplay_framebuffer_get_first_pixel_offset(self); +} + +STATIC bool sharpdisplay_framebuffer_get_pixels_in_byte_share_row(mp_obj_t self_in) { + sharpdisplay_framebuffer_obj_t *self = self_in; + return common_hal_sharpdisplay_framebuffer_get_pixels_in_byte_share_row(self); +} + +STATIC bool sharpdisplay_framebuffer_get_reverse_pixels_in_byte(mp_obj_t self_in) { + sharpdisplay_framebuffer_obj_t *self = self_in; + return common_hal_sharpdisplay_framebuffer_get_reverse_pixels_in_byte(self); +} + +STATIC int sharpdisplay_framebuffer_get_row_stride(mp_obj_t self_in) { + sharpdisplay_framebuffer_obj_t *self = self_in; + return common_hal_sharpdisplay_framebuffer_get_row_stride(self); +} + +STATIC void sharpdisplay_framebuffer_swapbuffers(mp_obj_t self_in, uint8_t *dirty_row_bitmask) { + sharpdisplay_framebuffer_obj_t *self = self_in; + common_hal_sharpdisplay_framebuffer_swapbuffers(self, dirty_row_bitmask); +} + +const framebuffer_p_t sharpdisplay_framebuffer_proto = { + MP_PROTO_IMPLEMENT(MP_QSTR_protocol_framebuffer) + .deinit = sharpdisplay_framebuffer_deinit, + .get_bufinfo = sharpdisplay_framebuffer_get_bufinfo, + .get_color_depth = sharpdisplay_framebuffer_get_color_depth, + .get_height = sharpdisplay_framebuffer_get_height, + .get_width = sharpdisplay_framebuffer_get_width, + .swapbuffers = sharpdisplay_framebuffer_swapbuffers, + + .get_first_pixel_offset = sharpdisplay_framebuffer_get_first_pixel_offset, + .get_pixels_in_byte_share_row = sharpdisplay_framebuffer_get_pixels_in_byte_share_row, + .get_reverse_pixels_in_byte = sharpdisplay_framebuffer_get_reverse_pixels_in_byte, + .get_row_stride = sharpdisplay_framebuffer_get_row_stride, +}; + +void common_hal_sharpdisplay_framebuffer_collect_ptrs(sharpdisplay_framebuffer_obj_t *self) { + gc_collect_ptr(self->framebuffer); + gc_collect_ptr(self->bus); + gc_collect_ptr(self->bufinfo.buf); +} diff --git a/shared-module/sharpdisplay/SharpMemoryFramebuffer.h b/shared-module/sharpdisplay/SharpMemoryFramebuffer.h new file mode 100644 index 000000000..8acacc94e --- /dev/null +++ b/shared-module/sharpdisplay/SharpMemoryFramebuffer.h @@ -0,0 +1,60 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2020 Jeff Epler for Adafruit Industries + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#pragma once + +#include "py/obj.h" +#include "shared-bindings/busio/SPI.h" +#include "shared-bindings/digitalio/DigitalInOut.h" +#include "shared-module/framebufferio/FramebufferDisplay.h" + +typedef struct { + mp_obj_base_t base; + mp_obj_t framebuffer; + busio_spi_obj_t* bus; + busio_spi_obj_t inline_bus; + digitalio_digitalinout_obj_t chip_select; + mp_buffer_info_t bufinfo; + + uint16_t width, height; + uint32_t baudrate; + + bool full_refresh:1; +} sharpdisplay_framebuffer_obj_t; + +void common_hal_sharpdisplay_framebuffer_construct(sharpdisplay_framebuffer_obj_t *self, busio_spi_obj_t *spi, mcu_pin_obj_t *chip_select, int baudrate, int width, int height); +void common_hal_sharpdisplay_framebuffer_swap_buffers(sharpdisplay_framebuffer_obj_t *self, uint8_t *dirty_row_bitmask); +void common_hal_sharpdisplay_framebuffer_deinit(sharpdisplay_framebuffer_obj_t *self); +void common_hal_sharpdisplay_framebuffer_get_bufinfo(sharpdisplay_framebuffer_obj_t *self, mp_buffer_info_t *bufinfo); +int common_hal_sharpdisplay_framebuffer_get_height(sharpdisplay_framebuffer_obj_t *self); +int common_hal_sharpdisplay_framebuffer_get_width(sharpdisplay_framebuffer_obj_t *self); +void common_hal_sharpdisplay_framebuffer_swap_buffers(sharpdisplay_framebuffer_obj_t *self, uint8_t *dirty_row_bitmask); +void common_hal_sharpdisplay_framebuffer_reset(sharpdisplay_framebuffer_obj_t *self); +void common_hal_sharpdisplay_framebuffer_reconstruct(sharpdisplay_framebuffer_obj_t *self); + +extern const framebuffer_p_t sharpdisplay_framebuffer_proto; + +void common_hal_sharpdisplay_framebuffer_collect_ptrs(sharpdisplay_framebuffer_obj_t*); diff --git a/shared-module/sharpdisplay/__init__.c b/shared-module/sharpdisplay/__init__.c new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/shared-module/sharpdisplay/__init__.c diff --git a/shared-module/sharpdisplay/__init__.h b/shared-module/sharpdisplay/__init__.h new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/shared-module/sharpdisplay/__init__.h diff --git a/shared-module/storage/__init__.c b/shared-module/storage/__init__.c index c3d4b50c8..5864307fd 100644 --- a/shared-module/storage/__init__.c +++ b/shared-module/storage/__init__.c @@ -3,7 +3,7 @@ * * The MIT License (MIT) * - * Copyright (c) 2013, 2014 Damien P. George + * SPDX-FileCopyrightText: Copyright (c) 2013, 2014 Damien P. George * Copyright (c) 2015 Josef Gajdusek * Copyright (c) 2017 Scott Shawcroft for Adafruit Industries * @@ -30,6 +30,7 @@ #include "extmod/vfs.h" #include "py/mperrno.h" +#include "py/mphal.h" #include "py/obj.h" #include "py/runtime.h" #include "shared-bindings/microcontroller/__init__.h" @@ -159,6 +160,8 @@ void common_hal_storage_remount(const char *mount_path, bool readonly, bool disa } void common_hal_storage_erase_filesystem(void) { + usb_disconnect(); + mp_hal_delay_ms(1000); filesystem_init(false, true); // Force a re-format. common_hal_mcu_reset(); // We won't actually get here, since we're resetting. diff --git a/shared-module/time/__init__.c b/shared-module/time/__init__.c new file mode 100644 index 000000000..347e68ae0 --- /dev/null +++ b/shared-module/time/__init__.c @@ -0,0 +1,45 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2016 Scott Shawcroft for Adafruit Industries + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "py/mphal.h" +#include "supervisor/port.h" +#include "supervisor/shared/tick.h" + +uint64_t common_hal_time_monotonic(void) { + return supervisor_ticks_ms64(); +} + +uint64_t common_hal_time_monotonic_ns(void) { + uint8_t subticks = 0; + uint64_t ticks = port_get_raw_ticks(&subticks); + // A tick is 976562.5 nanoseconds so multiply it by the base and add half instead of doing float + // math. + return 976562 * ticks + ticks / 2 + 30518 * subticks; +} + +void common_hal_time_delay_ms(uint32_t delay) { + mp_hal_delay_ms(delay); +} diff --git a/shared-module/touchio/TouchIn.c b/shared-module/touchio/TouchIn.c index 88d12b8b8..3fdf3e0ca 100644 --- a/shared-module/touchio/TouchIn.c +++ b/shared-module/touchio/TouchIn.c @@ -31,6 +31,7 @@ #include "py/runtime.h" #include "py/mphal.h" #include "shared-bindings/touchio/TouchIn.h" +#include "shared-bindings/microcontroller/Pin.h" // This is a capacitive touch sensing routine using a single digital // pin. The pin should be connected to the sensing pad, and to ground @@ -67,7 +68,7 @@ static uint16_t get_raw_reading(touchio_touchin_obj_t *self) { } void common_hal_touchio_touchin_construct(touchio_touchin_obj_t* self, const mcu_pin_obj_t *pin) { - claim_pin(pin); + common_hal_mcu_pin_claim(pin); self->digitalinout = m_new_obj(digitalio_digitalinout_obj_t); self->digitalinout->base.type = &digitalio_digitalinout_type; diff --git a/shared-module/usb_hid/Device.c b/shared-module/usb_hid/Device.c index 8744f2ed3..943c9bfed 100644 --- a/shared-module/usb_hid/Device.c +++ b/shared-module/usb_hid/Device.c @@ -25,7 +25,7 @@ */ #include <string.h> -#include "tick.h" + #include "py/runtime.h" #include "shared-bindings/usb_hid/Device.h" #include "shared-module/usb_hid/Device.h" @@ -84,14 +84,17 @@ 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) { + if (report_type == HID_REPORT_TYPE_INVALID) { + report_id = buffer[0]; + buffer++; + bufsize--; + } else if (report_type != HID_REPORT_TYPE_OUTPUT) { + return; + } + usb_hid_device_obj_t* hid_device = get_hid_device(report_id); - if ( report_type == HID_REPORT_TYPE_OUTPUT ) { - // Check if it is Keyboard device - if (hid_device->usage_page == HID_USAGE_PAGE_DESKTOP && - hid_device->usage == HID_USAGE_DESKTOP_KEYBOARD) { - // This is LED indicator (CapsLock, NumLock) - // TODO Light up some LED here - } + if (hid_device && hid_device->out_report_length >= bufsize) { + memcpy(hid_device->out_report_buffer, buffer, bufsize); } } diff --git a/shared-module/usb_hid/Device.h b/shared-module/usb_hid/Device.h index 10f2ee897..93c3518b4 100644 --- a/shared-module/usb_hid/Device.h +++ b/shared-module/usb_hid/Device.h @@ -43,6 +43,8 @@ typedef struct { uint8_t report_length; uint8_t usage_page; uint8_t usage; + uint8_t* out_report_buffer; + uint8_t out_report_length; } usb_hid_device_obj_t; diff --git a/shared-module/ustack/__init__.c b/shared-module/ustack/__init__.c index 947f81f8e..1e168ad6a 100644 --- a/shared-module/ustack/__init__.c +++ b/shared-module/ustack/__init__.c @@ -49,4 +49,3 @@ uint32_t shared_module_ustack_stack_size() { uint32_t shared_module_ustack_stack_usage() { return mp_stack_usage(); } - diff --git a/shared-module/vectorio/Circle.c b/shared-module/vectorio/Circle.c new file mode 100644 index 000000000..73629b8ce --- /dev/null +++ b/shared-module/vectorio/Circle.c @@ -0,0 +1,55 @@ + +#include "shared-bindings/vectorio/Circle.h" +#include "shared-module/vectorio/__init__.h" +#include "shared-module/displayio/area.h" + +#include "py/runtime.h" +#include "stdlib.h" + + +void common_hal_vectorio_circle_construct(vectorio_circle_t *self, uint16_t radius) { + self->radius = radius; + self->on_dirty.obj = NULL; +} + +void common_hal_vectorio_circle_set_on_dirty(vectorio_circle_t *self, vectorio_event_t on_dirty) { + if (self->on_dirty.obj != NULL) { + mp_raise_TypeError(translate("circle can only be registered in one parent")); + } + self->on_dirty = on_dirty; +} + + +uint32_t common_hal_vectorio_circle_get_pixel(void *obj, int16_t x, int16_t y) { + vectorio_circle_t *self = obj; + int16_t radius = abs(self->radius); + x = abs(x); + y = abs(y); + if (x+y <= radius) return 1; + if (x > radius) return 0; + if (y > radius) return 0; + const bool pythagorasSmallerThanRadius = (int32_t)x*x + (int32_t)y*y <= (int32_t)radius*radius; + return pythagorasSmallerThanRadius ? 1 : 0; +} + + +void common_hal_vectorio_circle_get_area(void *circle, displayio_area_t *out_area) { + vectorio_circle_t *self = circle; + out_area->x1 = -1 * self->radius - 1; + out_area->y1 = -1 * self->radius - 1; + out_area->x2 = self->radius + 1; + out_area->y2 = self->radius + 1; +} + +int16_t common_hal_vectorio_circle_get_radius(void *obj) { + vectorio_circle_t *self = obj; + return self->radius; +} + +void common_hal_vectorio_circle_set_radius(void *obj, int16_t radius) { + vectorio_circle_t *self = obj; + self->radius = abs(radius); + if (self->on_dirty.obj != NULL) { + self->on_dirty.event(self->on_dirty.obj); + } +} diff --git a/shared-module/vectorio/Circle.h b/shared-module/vectorio/Circle.h new file mode 100644 index 000000000..d6a77b166 --- /dev/null +++ b/shared-module/vectorio/Circle.h @@ -0,0 +1,16 @@ +#ifndef MICROPY_INCLUDED_SHARED_MODULE_VECTORIO_CIRCLE_H +#define MICROPY_INCLUDED_SHARED_MODULE_VECTORIO_CIRCLE_H + +#include <stdint.h> + +#include "py/obj.h" + +#include "shared-module/vectorio/__init__.h" + +typedef struct { + mp_obj_base_t base; + uint16_t radius; + vectorio_event_t on_dirty; +} vectorio_circle_t; + +#endif // MICROPY_INCLUDED_SHARED_MODULE_VECTORIO_CIRCLE_H diff --git a/shared-module/vectorio/Polygon.c b/shared-module/vectorio/Polygon.c new file mode 100644 index 000000000..e00a4696b --- /dev/null +++ b/shared-module/vectorio/Polygon.c @@ -0,0 +1,161 @@ + +#include "shared-module/vectorio/__init__.h" +#include "shared-bindings/vectorio/Polygon.h" +#include "shared-module/displayio/area.h" + +#include "py/runtime.h" +#include "py/gc.h" + +#include "stdlib.h" +#include <stdio.h> + + +#define VECTORIO_POLYGON_DEBUG(...) (void)0 +// #define VECTORIO_POLYGON_DEBUG(...) mp_printf(&mp_plat_print __VA_OPT__(,) __VA_ARGS__) + + +// Converts a list of points tuples to a flat list of ints for speedier internal use. +// Also validates the points. +static void _clobber_points_list(vectorio_polygon_t *self, mp_obj_t points_tuple_list) { + size_t len = 0; + mp_obj_t *items; + mp_obj_list_get(points_tuple_list, &len, &items); + VECTORIO_POLYGON_DEBUG(" self.len: %d, len: %d, ", self->len, len); + + if ( len < 3 ) { + mp_raise_TypeError_varg(translate("Polygon needs at least 3 points")); + } + + if ( self->len < 2*len ) { + if ( self->points_list != NULL ) { + VECTORIO_POLYGON_DEBUG("free(%d), ", sizeof(self->points_list)); + gc_free( self->points_list ); + } + self->points_list = gc_alloc( 2 * len * sizeof(int), false, false ); + VECTORIO_POLYGON_DEBUG("alloc(%p, %d)", self->points_list, 2 * len * sizeof(int)); + } + self->len = 2*len; + + for ( size_t i = 0; i < len; ++i) { + size_t tuple_len = 0; + mp_obj_t *tuple_items; + mp_obj_tuple_get(items[i], &tuple_len, &tuple_items); + + if (tuple_len != 2) { + mp_raise_ValueError_varg(translate("%q must be a tuple of length 2"), MP_QSTR_point); + } + if ( !mp_obj_get_int_maybe(tuple_items[ 0 ], &self->points_list[2*i ]) + || !mp_obj_get_int_maybe(tuple_items[ 1 ], &self->points_list[2*i + 1]) + ) { + self->len = 0; + gc_free( self->points_list ); + self->points_list = NULL; + mp_raise_ValueError_varg(translate("unsupported %q type"), MP_QSTR_point); + } + } +} + + + +void common_hal_vectorio_polygon_construct(vectorio_polygon_t *self, mp_obj_t points_list) { + VECTORIO_POLYGON_DEBUG("%p polygon_construct: ", self); + self->points_list = NULL; + self->len = 0; + self->on_dirty.obj = NULL; + _clobber_points_list( self, points_list ); + VECTORIO_POLYGON_DEBUG("\n"); +} + + +mp_obj_t common_hal_vectorio_polygon_get_points(vectorio_polygon_t *self) { + VECTORIO_POLYGON_DEBUG("%p common_hal_vectorio_polygon_get_points {len: %d, points_list: %p}\n", self, self->len, self->points_list); + mp_obj_t list = mp_obj_new_list(self->len/2, NULL); + + for (size_t i = 0; i < self->len; i += 2) { + mp_obj_t tuple[] = { mp_obj_new_int(self->points_list[i]), mp_obj_new_int(self->points_list[i+1]) }; + mp_obj_list_append( + list, + mp_obj_new_tuple(2, tuple) + ); + } + return list; +} +void common_hal_vectorio_polygon_set_points(vectorio_polygon_t *self, mp_obj_t points_list) { + VECTORIO_POLYGON_DEBUG("%p common_hal_vectorio_polygon_set_points: ", self); + _clobber_points_list( self, points_list ); + if (self->on_dirty.obj != NULL) { + self->on_dirty.event(self->on_dirty.obj); + } + VECTORIO_POLYGON_DEBUG("\n"); +} + +void common_hal_vectorio_polygon_set_on_dirty(vectorio_polygon_t *self, vectorio_event_t notification) { + if ( self->on_dirty.obj != NULL ) { + mp_raise_TypeError(translate("polygon can only be registered in one parent")); + } + self->on_dirty = notification; +} + + +void common_hal_vectorio_polygon_get_area(void *polygon, displayio_area_t *area) { + vectorio_polygon_t *self = polygon; + + area->x1 = SHRT_MAX; + area->y1 = SHRT_MAX; + area->x2 = SHRT_MIN; + area->y2 = SHRT_MIN; + for (size_t i=0; i < self->len; ++i) { + int x = self->points_list[i]; + ++i; + int y = self->points_list[i]; + if (x <= area->x1) area->x1 = x-1; + if (y <= area->y1) area->y1 = y-1; + if (x >= area->x2) area->x2 = x+1; + if (y >= area->y2) area->y2 = y+1; + } +} + + +// <0 if the point is to the left of the line vector +// 0 if the point is on the line +// >0 if the point is to the right of the line vector +__attribute__((always_inline)) static inline int line_side( mp_int_t x1, mp_int_t y1, mp_int_t x2, mp_int_t y2, int16_t px, int16_t py ) { + return (px - x1) * (y2 - y1) + - (py - y1) * (x2 - x1); +} + + +uint32_t common_hal_vectorio_polygon_get_pixel(void *obj, int16_t x, int16_t y) { + VECTORIO_POLYGON_DEBUG("%p polygon get_pixel %d, %d\n", obj, x, y); + vectorio_polygon_t *self = obj; + + if (self->len == 0) { + return 0; + } + + int winding_number = 0; + int x1 = self->points_list[0]; + int y1 = self->points_list[1]; + for (size_t i=2; i <= self->len + 1; ++i) { + VECTORIO_POLYGON_DEBUG(" {(%3d, %3d),", x1, y1); + int x2 = self->points_list[i % self->len]; + ++i; + int y2 = self->points_list[i % self->len]; + VECTORIO_POLYGON_DEBUG(" (%3d, %3d)}\n", x2, y2); + if ( y1 <= y ) { + if ( y2 > y && line_side(x1, y1, x2, y2, x, y) < 0 ) { + // Wind up, point is to the left of the edge vector + ++winding_number; + VECTORIO_POLYGON_DEBUG(" wind:%2d winding_number:%2d\n", 1, winding_number); + } + } else if ( y2 <= y && line_side(x1, y1, x2, y2, x, y) > 0 ) { + // Wind down, point is to the right of the edge vector + --winding_number; + VECTORIO_POLYGON_DEBUG(" wind:%2d winding_number:%2d\n", -1, winding_number); + } + + x1 = x2; + y1 = y2; + } + return winding_number == 0 ? 0 : 1; +} diff --git a/shared-module/vectorio/Polygon.h b/shared-module/vectorio/Polygon.h new file mode 100644 index 000000000..70de9036d --- /dev/null +++ b/shared-module/vectorio/Polygon.h @@ -0,0 +1,17 @@ +#ifndef MICROPY_INCLUDED_SHARED_MODULE_VECTORIO_POLYGON_H +#define MICROPY_INCLUDED_SHARED_MODULE_VECTORIO_POLYGON_H + +#include <stdint.h> + +#include "py/obj.h" +#include "shared-module/vectorio/__init__.h" + +typedef struct { + mp_obj_base_t base; + // An int array[ x, y, ... ] + int *points_list; + size_t len; + vectorio_event_t on_dirty; +} vectorio_polygon_t; + +#endif // MICROPY_INCLUDED_SHARED_MODULE_VECTORIO_POLYGON_H diff --git a/shared-module/vectorio/Rectangle.c b/shared-module/vectorio/Rectangle.c new file mode 100644 index 000000000..5d48cc648 --- /dev/null +++ b/shared-module/vectorio/Rectangle.c @@ -0,0 +1,34 @@ +#include "shared-bindings/vectorio/Rectangle.h" +#include "shared-module/displayio/area.h" + +#include "py/runtime.h" + + +void common_hal_vectorio_rectangle_construct(vectorio_rectangle_t *self, uint32_t width, uint32_t height) { + self->width = width; + self->height = height; +} + + +uint32_t common_hal_vectorio_rectangle_get_pixel(void *obj, int16_t x, int16_t y) { + vectorio_rectangle_t *self = obj; + if (x < 0 || x > self->width || y > self->height || y < 0) { + return 0; + } + return 1; +} + + +void common_hal_vectorio_rectangle_get_area(void *rectangle, displayio_area_t *out_area) { + vectorio_rectangle_t *self = rectangle; + out_area->x1 = -1; + out_area->y1 = -1; + out_area->x2 = self->width; + out_area->y2 = self->height; +} + + +uint32_t common_hal_vectorio_rectangle_get_height(void *rectangle) { + vectorio_rectangle_t *self = rectangle; + return self->height; +} diff --git a/shared-module/vectorio/Rectangle.h b/shared-module/vectorio/Rectangle.h new file mode 100644 index 000000000..56342a6d7 --- /dev/null +++ b/shared-module/vectorio/Rectangle.h @@ -0,0 +1,14 @@ +#ifndef MICROPY_INCLUDED_SHARED_MODULE_VECTORIO_RECTANGLE_H +#define MICROPY_INCLUDED_SHARED_MODULE_VECTORIO_RECTANGLE_H + +#include <stdint.h> + +#include "py/obj.h" + +typedef struct { + mp_obj_base_t base; + uint16_t width; + uint16_t height; +} vectorio_rectangle_t; + +#endif // MICROPY_INCLUDED_SHARED_MODULE_VECTORIO_RECTANGLE_H diff --git a/shared-module/vectorio/VectorShape.c b/shared-module/vectorio/VectorShape.c new file mode 100644 index 000000000..81f46b3fa --- /dev/null +++ b/shared-module/vectorio/VectorShape.c @@ -0,0 +1,334 @@ + +#include "stdlib.h" + +#include "shared-module/vectorio/__init__.h" +#include "shared-bindings/vectorio/VectorShape.h" + +#include "py/runtime.h" +#include "shared-bindings/time/__init__.h" +#include "shared-bindings/displayio/ColorConverter.h" +#include "shared-bindings/displayio/Palette.h" + +#include "shared-bindings/vectorio/Circle.h" +#include "shared-bindings/vectorio/Polygon.h" +#include "shared-bindings/vectorio/Rectangle.h" + +// Lifecycle actions. +#define VECTORIO_SHAPE_DEBUG(...) (void)0 +// #define VECTORIO_SHAPE_DEBUG(...) mp_printf(&mp_plat_print __VA_OPT__(,) __VA_ARGS__) + + +// Used in both logging and ifdefs, for extra variables +// #define VECTORIO_PERF(...) mp_printf(&mp_plat_print __VA_OPT__(,) __VA_ARGS__) + + +// Really verbose. +#define VECTORIO_SHAPE_PIXEL_DEBUG(...) (void)0 +// #define VECTORIO_SHAPE_PIXEL_DEBUG(...) mp_printf(&mp_plat_print __VA_OPT__(,) __VA_ARGS__) + + +inline __attribute__((always_inline)) +static int32_t max(int32_t a, int32_t b) { + return a > b ? a : b; +} + + +inline __attribute__((always_inline)) +static void _get_screen_area(vectorio_vector_shape_t *self, displayio_area_t *out_area) { + VECTORIO_SHAPE_DEBUG("%p get_screen_area tform:{x:%d y:%d dx:%d dy:%d scl:%d w:%d h:%d mx:%d my:%d tr:%d}", self, + self->absolute_transform->x, self->absolute_transform->y, self->absolute_transform->dx, self->absolute_transform->dy, self->absolute_transform->scale, + self->absolute_transform->width, self->absolute_transform->height, self->absolute_transform->mirror_x, self->absolute_transform->mirror_y, self->absolute_transform->transpose_xy + ); + self->ishape.get_area(self->ishape.shape, out_area); + VECTORIO_SHAPE_DEBUG(" in:{(%5d,%5d), (%5d,%5d)}", out_area->x1, out_area->y1, out_area->x2, out_area->y2); + if (self->absolute_transform->transpose_xy) { + int16_t swap = out_area->x1; + out_area->x1 = (out_area->y1 + self->y) * self->absolute_transform->dx + self->absolute_transform->x; + out_area->y1 = (swap + self->x) * self->absolute_transform->dy + self->absolute_transform->y; + swap = out_area->x2; + out_area->x2 = (out_area->y2 + self->y) * self->absolute_transform->dx + self->absolute_transform->x; + out_area->y2 = (swap + self->x) * self->absolute_transform->dy + self->absolute_transform->y; + } else { + out_area->x1 = (out_area->x1 + self->x) * self->absolute_transform->dx + self->absolute_transform->x; + out_area->y1 = (out_area->y1 + self->y) * self->absolute_transform->dy + self->absolute_transform->y; + out_area->x2 = (out_area->x2 + self->x) * self->absolute_transform->dx + self->absolute_transform->x; + out_area->y2 = (out_area->y2 + self->y) * self->absolute_transform->dy + self->absolute_transform->y; + } + // We might have mirrored due to dx + if (out_area->x2 < out_area->x1) { + int16_t swap = out_area->x1; + out_area->x1 = out_area->x2; + out_area->x2 = swap; + } + if (out_area->y2 < out_area->y1) { + int16_t swap = out_area->y1; + out_area->y1 = out_area->y2; + out_area->y2 = swap; + } + VECTORIO_SHAPE_DEBUG(" out:{(%5d,%5d), (%5d,%5d)}\n", out_area->x1, out_area->y1, out_area->x2, out_area->y2); +} + + +// For use by Group to know where it needs to redraw on layer removal. +bool vectorio_vector_shape_get_dirty_area(vectorio_vector_shape_t *self, displayio_area_t *out_area) { + displayio_area_copy(&self->ephemeral_dirty_area, out_area); + return true; // For now just always redraw. +} + + +// This must be invoked each time a shape changes its position or its shape in any way. +void common_hal_vectorio_vector_shape_set_dirty(void *vector_shape) { + vectorio_vector_shape_t *self = vector_shape; + // In screen space. Need to offset the shape space. + displayio_area_t current_area; + _get_screen_area(self, ¤t_area); + VECTORIO_SHAPE_DEBUG("%p shape_dirty current:{(%3d,%3d), (%3d,%3d)} dirty:{(%3d,%3d), (%3d,%3d)}", + self, + current_area.x1, current_area.y1, current_area.x2, current_area.y2, + self->ephemeral_dirty_area.x1, self->ephemeral_dirty_area.y1, self->ephemeral_dirty_area.x2, self->ephemeral_dirty_area.y2); + self->dirty = true; + // Dirty area tracks the shape's footprint between draws. It's reset on refresh finish, + displayio_area_expand(&self->ephemeral_dirty_area, ¤t_area); + VECTORIO_SHAPE_DEBUG(" -> expanded:{(%3d,%3d), (%3d,%3d)}\n", self->ephemeral_dirty_area.x1, self->ephemeral_dirty_area.y1, self->ephemeral_dirty_area.x2, self->ephemeral_dirty_area.y2); +} + + +static displayio_buffer_transform_t null_transform = { + .x = 0, + .y = 0, + .dx = 1, + .dy = 1, + .scale = 1, + .width = 0, + .height = 0, + .mirror_x = false, + .mirror_y = false, + .transpose_xy = false +}; + + +void common_hal_vectorio_vector_shape_construct(vectorio_vector_shape_t *self, + vectorio_ishape_t ishape, + mp_obj_t pixel_shader, uint16_t x, uint16_t y) { + VECTORIO_SHAPE_DEBUG("%p vector_shape_construct x:%3d, y:%3d\n", self, x, y); + self->x = x; + self->y = y; + self->pixel_shader = pixel_shader; + self->ishape = ishape; + self->dirty = true; + self->absolute_transform = &null_transform; // Critical to have a valid transform before getting screen area. + _get_screen_area(self, &self->ephemeral_dirty_area); + self->ephemeral_dirty_area.next = NULL; +} + + +mp_int_t common_hal_vectorio_vector_shape_get_x(vectorio_vector_shape_t *self) { + VECTORIO_SHAPE_DEBUG("%p get_x\n", self); + return self->x; +} + + +void common_hal_vectorio_vector_shape_set_x(vectorio_vector_shape_t *self, mp_int_t x) { + VECTORIO_SHAPE_DEBUG("%p set_x %d\n", self, x); + if (self->x == x) { + return; + } + self->x = x; + common_hal_vectorio_vector_shape_set_dirty(self); +} + + +mp_int_t common_hal_vectorio_vector_shape_get_y(vectorio_vector_shape_t *self) { + VECTORIO_SHAPE_DEBUG("%p get_y\n", self); + return self->y; +} + + +void common_hal_vectorio_vector_shape_set_y(vectorio_vector_shape_t *self, mp_int_t y) { + VECTORIO_SHAPE_DEBUG("%p set_y %d\n", self, y); + if (self->y == y) { + return; + } + self->y = y; + common_hal_vectorio_vector_shape_set_dirty(self); +} + + +mp_obj_t common_hal_vectorio_vector_shape_get_pixel_shader(vectorio_vector_shape_t *self) { + VECTORIO_SHAPE_DEBUG("%p get_pixel_shader\n", self); + return self->pixel_shader; +} + +void common_hal_vectorio_vector_shape_set_pixel_shader(vectorio_vector_shape_t *self, mp_obj_t pixel_shader) { + VECTORIO_SHAPE_DEBUG("%p set_pixel_shader\n", self); + self->pixel_shader = pixel_shader; + common_hal_vectorio_vector_shape_set_dirty(self); +} + + +bool vectorio_vector_shape_fill_area(vectorio_vector_shape_t *self, const _displayio_colorspace_t* colorspace, const displayio_area_t* area, uint32_t* mask, uint32_t *buffer) { + // Shape areas are relative to 0,0. This will allow rotation about a known axis. + // The consequence is that the area reported by the shape itself is _relative_ to 0,0. + // To make it relative to the VectorShape position, we must shift it. + // Pixels are drawn on the screen_area (shifted) coordinate space, while pixels are _determined_ from + // the shape_area (unshifted) space. +#ifdef VECTORIO_PERF + uint64_t start = common_hal_time_monotonic_ns(); + uint64_t pixel_time = 0; +#endif + displayio_area_t overlap; + VECTORIO_SHAPE_DEBUG("%p fill_area dirty:%d fill: {(%5d,%5d), (%5d,%5d)} dirty: {(%5d,%5d), (%5d,%5d)}", + self, self->dirty, + area->x1, area->y1, area->x2, area->y2, + self->ephemeral_dirty_area.x1, self->ephemeral_dirty_area.y1, self->ephemeral_dirty_area.x2, self->ephemeral_dirty_area.y2 + ); + if (!displayio_area_compute_overlap(area, &self->ephemeral_dirty_area, &overlap)) { + VECTORIO_SHAPE_DEBUG(" no overlap\n"); + return false; + } + VECTORIO_SHAPE_DEBUG(", overlap: {(%3d,%3d), (%3d,%3d)}", overlap.x1, overlap.y1, overlap.x2, overlap.y2); + + bool full_coverage = displayio_area_equal(area, &overlap); + + uint8_t pixels_per_byte = 8 / colorspace->depth; + + uint32_t linestride_px = displayio_area_width(area); + uint32_t line_dirty_offset_px = (overlap.y1 - area->y1) * linestride_px; + uint32_t column_dirty_offset_px = overlap.x1 - area->x1; + VECTORIO_SHAPE_DEBUG(", linestride:%3d line_offset:%3d col_offset:%3d depth:%2d ppb:%2d shape:%s", + linestride_px, line_dirty_offset_px, column_dirty_offset_px, colorspace->depth, pixels_per_byte, mp_obj_get_type_str(self->ishape.shape)); + + displayio_input_pixel_t input_pixel; + displayio_output_pixel_t output_pixel; + + uint32_t mask_start_px = line_dirty_offset_px; + for (input_pixel.y = overlap.y1; input_pixel.y < overlap.y2; ++input_pixel.y) { + mask_start_px += column_dirty_offset_px; + for (input_pixel.x = overlap.x1; input_pixel.x < overlap.x2; ++input_pixel.x) { + // Check the mask first to see if the pixel has already been set. + uint32_t pixel_index = mask_start_px + (input_pixel.x - overlap.x1); + uint32_t *mask_doubleword = &(mask[pixel_index / 32]); + uint8_t mask_bit = pixel_index % 32; + VECTORIO_SHAPE_PIXEL_DEBUG("%p pixel_index: %5u mask_bit: %2u", self, pixel_index, mask_bit); + if ((*mask_doubleword & (1u << mask_bit)) != 0) { + VECTORIO_SHAPE_PIXEL_DEBUG(" masked\n"); + continue; + } + output_pixel.pixel = 0; + + // Get the target pixel based on the shape's coordinate space + int16_t pixel_to_get_x; + int16_t pixel_to_get_y; + if (self->absolute_transform->transpose_xy) { + pixel_to_get_x = (input_pixel.y - self->absolute_transform->dy * self->x - self->absolute_transform->y) / self->absolute_transform->dy; + pixel_to_get_y = (input_pixel.x - self->absolute_transform->dx * self->y - self->absolute_transform->x) / self->absolute_transform->dx; + } else { + pixel_to_get_x = (input_pixel.x - self->absolute_transform->dx * self->x - self->absolute_transform->x) / self->absolute_transform->dx; + pixel_to_get_y = (input_pixel.y - self->absolute_transform->dy * self->y - self->absolute_transform->y) / self->absolute_transform->dy; + } + VECTORIO_SHAPE_PIXEL_DEBUG(" get_pixel %p (%3d, %3d) -> ( %3d, %3d )", self->ishape.shape, input_pixel.x, input_pixel.y, pixel_to_get_x, pixel_to_get_y); +#ifdef VECTORIO_PERF + uint64_t pre_pixel = common_hal_time_monotonic_ns(); +#endif + input_pixel.pixel = self->ishape.get_pixel(self->ishape.shape, pixel_to_get_x, pixel_to_get_y); +#ifdef VECTORIO_PERF + uint64_t post_pixel = common_hal_time_monotonic_ns(); + pixel_time += post_pixel - pre_pixel; +#endif + VECTORIO_SHAPE_PIXEL_DEBUG(" -> %d", input_pixel.pixel); + + output_pixel.opaque = true; + if (self->pixel_shader == mp_const_none) { + output_pixel.pixel = input_pixel.pixel; + } else if (MP_OBJ_IS_TYPE(self->pixel_shader, &displayio_palette_type)) { + output_pixel.opaque = displayio_palette_get_color(self->pixel_shader, colorspace, input_pixel.pixel, &output_pixel.pixel); + } else if (MP_OBJ_IS_TYPE(self->pixel_shader, &displayio_colorconverter_type)) { + displayio_colorconverter_convert(self->pixel_shader, colorspace, &input_pixel, &output_pixel); + } + if (!output_pixel.opaque) { + VECTORIO_SHAPE_PIXEL_DEBUG(" (encountered transparent pixel; input area is not fully covered)\n"); + full_coverage = false; + } else { + *mask_doubleword |= 1u << mask_bit; + if (colorspace->depth == 16) { + VECTORIO_SHAPE_PIXEL_DEBUG(" buffer = %04x 16\n", output_pixel.pixel); + *(((uint16_t*) buffer) + pixel_index) = output_pixel.pixel; + } else if (colorspace->depth == 8) { + VECTORIO_SHAPE_PIXEL_DEBUG(" buffer = %02x 8\n", output_pixel.pixel); + *(((uint8_t*) buffer) + pixel_index) = output_pixel.pixel; + } else if (colorspace->depth < 8) { + // Reorder the offsets to pack multiple rows into a byte (meaning they share a column). + if (!colorspace->pixels_in_byte_share_row) { + uint16_t width = linestride_px; + uint16_t row = pixel_index / width; + uint16_t col = pixel_index % width; + pixel_index = col * pixels_per_byte + (row / pixels_per_byte) * pixels_per_byte * width + row % pixels_per_byte; + } + uint8_t shift = (pixel_index % pixels_per_byte) * colorspace->depth; + if (colorspace->reverse_pixels_in_byte) { + // Reverse the shift by subtracting it from the leftmost shift. + shift = (pixels_per_byte - 1) * colorspace->depth - shift; + } + VECTORIO_SHAPE_PIXEL_DEBUG(" buffer = %2d %d\n", output_pixel.pixel, colorspace->depth); + ((uint8_t*)buffer)[pixel_index / pixels_per_byte] |= output_pixel.pixel << shift; + } + } + } + mask_start_px += linestride_px - column_dirty_offset_px; + } +#ifdef VECTORIO_PERF + uint64_t end = common_hal_time_monotonic_ns(); + uint32_t pixels = (overlap.x2 - overlap.x1) * (overlap.y2 - overlap.y1); + VECTORIO_PERF("draw %16s -> shape:{%4dpx, %4.1fms,%9.1fpps fill} shape_pixels:{%6.1fus total, %4.1fus/px}\n", + mp_obj_get_type_str(self->ishape.shape), + (overlap.x2 - overlap.x1) * (overlap.y2 - overlap.y1), + (double)((end - start) / 1000000.0), + (double)(max(1, pixels * (1000000000.0 / (end - start)))), + (double)(pixel_time / 1000.0), + (double)(pixel_time / 1000.0 / pixels) + ); +#endif + VECTORIO_SHAPE_DEBUG(" -> pixels:%4d\n"); + return full_coverage; +} + + +void vectorio_vector_shape_finish_refresh(vectorio_vector_shape_t *self) { + if ( !self->dirty ) { + return; + } + VECTORIO_SHAPE_DEBUG("%p finish_refresh was:{(%3d,%3d), (%3d,%3d)}\n", self, self->ephemeral_dirty_area.x1, self->ephemeral_dirty_area.y1, self->ephemeral_dirty_area.x2, self->ephemeral_dirty_area.y2); + self->dirty = false; + // Reset dirty area tracking to current footprint + _get_screen_area(self, &self->ephemeral_dirty_area); + self->ephemeral_dirty_area.next = NULL; + VECTORIO_SHAPE_DEBUG("%p finish_refresh now:{(%3d,%3d), (%3d,%3d)}\n", self, self->ephemeral_dirty_area.x1, self->ephemeral_dirty_area.y1, self->ephemeral_dirty_area.x2, self->ephemeral_dirty_area.y2); + + if (MP_OBJ_IS_TYPE(self->pixel_shader, &displayio_palette_type)) { + displayio_palette_finish_refresh(self->pixel_shader); + } else if (MP_OBJ_IS_TYPE(self->pixel_shader, &displayio_colorconverter_type)) { + displayio_colorconverter_finish_refresh(self->pixel_shader); + } +} + + +// Assembles a singly linked list of dirty areas from all components on the display. +displayio_area_t* vectorio_vector_shape_get_refresh_areas(vectorio_vector_shape_t *self, displayio_area_t* tail) { + if (self->dirty + || (MP_OBJ_IS_TYPE(self->pixel_shader, &displayio_palette_type) && displayio_palette_needs_refresh(self->pixel_shader)) + || (MP_OBJ_IS_TYPE(self->pixel_shader, &displayio_colorconverter_type) && displayio_colorconverter_needs_refresh(self->pixel_shader)) + ) { + VECTORIO_SHAPE_DEBUG("%p get_refresh_area dirty:%d {(%3d,%3d), (%3d,%3d)}", self, self->dirty, self->ephemeral_dirty_area.x1, self->ephemeral_dirty_area.y1, self->ephemeral_dirty_area.x2, self->ephemeral_dirty_area.y2); + common_hal_vectorio_vector_shape_set_dirty(self); + // vector.add_to_head + self->ephemeral_dirty_area.next = tail; + VECTORIO_SHAPE_DEBUG(" this_area: %p next: %p after: %p\n", &self->ephemeral_dirty_area, tail, tail == NULL ? NULL : tail->next); + return &self->ephemeral_dirty_area; + } + return tail; +} + +void vectorio_vector_shape_update_transform(vectorio_vector_shape_t *self, displayio_buffer_transform_t *group_transform) { + self->absolute_transform = group_transform == NULL ? &null_transform : group_transform; + common_hal_vectorio_vector_shape_set_dirty(self); +} diff --git a/shared-module/vectorio/VectorShape.h b/shared-module/vectorio/VectorShape.h new file mode 100644 index 000000000..56eb3d8a5 --- /dev/null +++ b/shared-module/vectorio/VectorShape.h @@ -0,0 +1,53 @@ + +#ifndef MICROPY_INCLUDED_SHARED_MODULE_VECTORIO_SHAPE_H +#define MICROPY_INCLUDED_SHARED_MODULE_VECTORIO_SHAPE_H + +#include <stdbool.h> +#include <stdint.h> + +#include "py/obj.h" +#include "shared-module/displayio/area.h" +#include "shared-module/displayio/Palette.h" + +typedef void get_area_function(mp_obj_t shape, displayio_area_t *out_area); +typedef uint32_t get_pixel_function(mp_obj_t shape, int16_t x, int16_t y); + +// This struct binds a shape's common Shape support functions (its vector shape interface) +// to its instance pointer. We only check at construction time what the type of the +// associated shape is and link the correct functions up. +// Later when using the shape for drawing logic these functions may be invoked +// unconditionally. This simplifies the addition of new types and restricts the +// respective responsibilities of VectorShape and actual shape implementations. +typedef struct { + mp_obj_t shape; + get_area_function *get_area; + get_pixel_function *get_pixel; +} vectorio_ishape_t; + +typedef struct { + mp_obj_base_t base; + vectorio_ishape_t ishape; + mp_obj_t pixel_shader; + int16_t x; + int16_t y; + displayio_buffer_transform_t *absolute_transform; + bool dirty; // True if we need to draw + // Tracks current shape footprint and expands outward as the shape dirties and changes. + // This is suboptimal if you move your shape far. Could add more state to only redraw + // exactly what we left behind. + displayio_area_t ephemeral_dirty_area; +} vectorio_vector_shape_t; + +displayio_area_t* vectorio_vector_shape_get_refresh_areas(vectorio_vector_shape_t *self, displayio_area_t *tail); + +bool vectorio_vector_shape_get_dirty_area(vectorio_vector_shape_t *self, displayio_area_t *current_dirty_area); + +// Area is always in absolute screen coordinates. +bool vectorio_vector_shape_fill_area(vectorio_vector_shape_t *self, const _displayio_colorspace_t *colorspace, const displayio_area_t *area, uint32_t *mask, uint32_t *buffer); + +// Fills in out_area with the maximum bounds of all related pixels in the last rendered frame. Returns +// false if the vector shape wasn't rendered in the last frame. +bool vectorio_vector_shape_get_previous_area(vectorio_vector_shape_t *self, displayio_area_t *out_area); +void vectorio_vector_shape_finish_refresh(vectorio_vector_shape_t *self); + +#endif // MICROPY_INCLUDED_SHARED_MODULE_VECTORIO_SHAPE_H diff --git a/shared-module/vectorio/__init__.c b/shared-module/vectorio/__init__.c new file mode 100644 index 000000000..f5227ef01 --- /dev/null +++ b/shared-module/vectorio/__init__.c @@ -0,0 +1,2 @@ + +// Don't need anything in here yet diff --git a/shared-module/vectorio/__init__.h b/shared-module/vectorio/__init__.h new file mode 100644 index 000000000..8da85bba4 --- /dev/null +++ b/shared-module/vectorio/__init__.h @@ -0,0 +1,14 @@ +#ifndef MICROPY_INCLUDED_SHARED_MODULE_VECTORIO_INIT_H +#define MICROPY_INCLUDED_SHARED_MODULE_VECTORIO_INIT_H + +#include "py/obj.h" + +typedef void event_function(mp_obj_t obj); + +typedef struct { + mp_obj_t obj; + event_function *event; +} vectorio_event_t; + + +#endif diff --git a/shared-module/wiznet/wiznet5k.c b/shared-module/wiznet/wiznet5k.c index a603a5593..8ccb4ff8d 100644 --- a/shared-module/wiznet/wiznet5k.c +++ b/shared-module/wiznet/wiznet5k.c @@ -3,7 +3,7 @@ * * The MIT License (MIT) * - * Copyright (c) 2014 Damien P. George + * SPDX-FileCopyrightText: Copyright (c) 2014 Damien P. George * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal diff --git a/shared-module/wiznet/wiznet5k.h b/shared-module/wiznet/wiznet5k.h index 19823ae55..4597a3016 100644 --- a/shared-module/wiznet/wiznet5k.h +++ b/shared-module/wiznet/wiznet5k.h @@ -3,7 +3,7 @@ * * The MIT License (MIT) * - * Copyright (c) 2014 Damien P. George + * SPDX-FileCopyrightText: Copyright (c) 2014 Damien P. George * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal |
