summaryrefslogtreecommitdiff
path: root/shared-module
diff options
context:
space:
mode:
authorjgillick <j_gillick@yahoo.com>2020-11-17 00:02:24 -0800
committerjgillick <j_gillick@yahoo.com>2020-11-17 00:02:24 -0800
commit5f0a372a2206c14e4ae206ec7cb879265aa7a38a (patch)
tree7d0e5fd15aedbf28c5fd5a8d685928ed23146b7d /shared-module
parent119e9d38202cb26f7f04d1050263e27e1083865a (diff)
parent42ca57ff8f19368dff97bebd9ca0ac061b219084 (diff)
Merge tag '6.0.0' into thunderpack1.2
Diffstat (limited to 'shared-module')
-rw-r--r--shared-module/_bleio/Characteristic.h13
-rw-r--r--shared-module/_bleio/ScanEntry.c14
-rw-r--r--shared-module/_bleio/ScanResults.c10
-rw-r--r--shared-module/_eve/__init__.c1
-rw-r--r--shared-module/_pew/PewPew.c125
-rw-r--r--shared-module/_pew/__init__.c87
-rw-r--r--shared-module/_pixelbuf/PixelBuf.c67
-rw-r--r--shared-module/_stage/__init__.c2
-rw-r--r--shared-module/aesio/__init__.c58
-rw-r--r--shared-module/aesio/__init__.h59
-rw-r--r--shared-module/aesio/aes.c608
-rw-r--r--shared-module/aesio/aes.h105
-rw-r--r--shared-module/audiomixer/Mixer.c21
-rw-r--r--shared-module/audiomp3/MP3Decoder.c54
-rw-r--r--shared-module/audiomp3/MP3Decoder.h2
-rw-r--r--shared-module/bitbangio/I2C.c2
-rw-r--r--shared-module/bitbangio/SPI.c2
-rw-r--r--shared-module/board/__init__.c46
-rw-r--r--shared-module/canio/Match.c43
-rw-r--r--shared-module/canio/Match.h (renamed from shared-module/_pew/__init__.h)14
-rw-r--r--shared-module/canio/Message.c77
-rw-r--r--shared-module/canio/Message.h37
-rw-r--r--shared-module/canio/RemoteTransmissionRequest.c67
-rw-r--r--shared-module/canio/RemoteTransmissionRequest.h33
-rw-r--r--shared-module/displayio/Bitmap.c26
-rw-r--r--shared-module/displayio/ColorConverter.c5
-rw-r--r--shared-module/displayio/Display.c88
-rw-r--r--shared-module/displayio/Display.h10
-rw-r--r--shared-module/displayio/EPaperDisplay.c13
-rw-r--r--shared-module/displayio/EPaperDisplay.h1
-rw-r--r--shared-module/displayio/FourWire.c10
-rw-r--r--shared-module/displayio/Group.c50
-rw-r--r--shared-module/displayio/I2CDisplay.c7
-rw-r--r--shared-module/displayio/Palette.c2
-rw-r--r--shared-module/displayio/Shape.c78
-rw-r--r--shared-module/displayio/Shape.h5
-rw-r--r--shared-module/displayio/TileGrid.c10
-rw-r--r--shared-module/displayio/__init__.c47
-rw-r--r--shared-module/displayio/__init__.h10
-rw-r--r--shared-module/displayio/display_core.c46
-rw-r--r--shared-module/displayio/display_core.h8
-rw-r--r--shared-module/framebufferio/FramebufferDisplay.c121
-rw-r--r--shared-module/framebufferio/FramebufferDisplay.h49
-rw-r--r--shared-module/gamepad/GamePad.c2
-rw-r--r--shared-module/gamepad/__init__.c4
-rw-r--r--shared-module/gamepadshift/GamePadShift.c2
-rw-r--r--shared-module/ipaddress/IPv4Address.c39
-rw-r--r--shared-module/ipaddress/IPv4Address.h (renamed from shared-module/_pew/PewPew.h)25
-rw-r--r--shared-module/ipaddress/__init__.c35
-rw-r--r--shared-module/ipaddress/__init__.h36
-rw-r--r--shared-module/memorymonitor/AllocationAlarm.c94
-rw-r--r--shared-module/memorymonitor/AllocationAlarm.h51
-rw-r--r--shared-module/memorymonitor/AllocationSize.c91
-rw-r--r--shared-module/memorymonitor/AllocationSize.h51
-rw-r--r--shared-module/memorymonitor/__init__.c39
-rw-r--r--shared-module/memorymonitor/__init__.h35
-rw-r--r--shared-module/network/__init__.c6
-rw-r--r--shared-module/network/__init__.h2
-rw-r--r--shared-module/os/__init__.c2
-rw-r--r--shared-module/rgbmatrix/RGBMatrix.c79
-rw-r--r--shared-module/rgbmatrix/RGBMatrix.h47
-rw-r--r--shared-module/rgbmatrix/allocator.h54
-rw-r--r--shared-module/sdcardio/SDCard.c466
-rw-r--r--shared-module/sdcardio/SDCard.h51
-rw-r--r--shared-module/sdcardio/__init__.c0
-rw-r--r--shared-module/sdcardio/__init__.h0
-rw-r--r--shared-module/sharpdisplay/SharpMemoryFramebuffer.c275
-rw-r--r--shared-module/sharpdisplay/SharpMemoryFramebuffer.h60
-rw-r--r--shared-module/sharpdisplay/__init__.c0
-rw-r--r--shared-module/sharpdisplay/__init__.h0
-rw-r--r--shared-module/storage/__init__.c5
-rw-r--r--shared-module/time/__init__.c45
-rw-r--r--shared-module/touchio/TouchIn.c3
-rw-r--r--shared-module/usb_hid/Device.c19
-rw-r--r--shared-module/usb_hid/Device.h2
-rw-r--r--shared-module/ustack/__init__.c1
-rw-r--r--shared-module/vectorio/Circle.c55
-rw-r--r--shared-module/vectorio/Circle.h16
-rw-r--r--shared-module/vectorio/Polygon.c161
-rw-r--r--shared-module/vectorio/Polygon.h17
-rw-r--r--shared-module/vectorio/Rectangle.c34
-rw-r--r--shared-module/vectorio/Rectangle.h14
-rw-r--r--shared-module/vectorio/VectorShape.c334
-rw-r--r--shared-module/vectorio/VectorShape.h53
-rw-r--r--shared-module/vectorio/__init__.c2
-rw-r--r--shared-module/vectorio/__init__.h14
-rw-r--r--shared-module/wiznet/wiznet5k.c2
-rw-r--r--shared-module/wiznet/wiznet5k.h2
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, &current_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, &current_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