diff --git a/locale/circuitpython.pot b/locale/circuitpython.pot index e2bbbd962a..835ef9538b 100644 --- a/locale/circuitpython.pot +++ b/locale/circuitpython.pot @@ -574,6 +574,7 @@ msgstr "" #: shared-bindings/displayio/Display.c #: shared-bindings/framebufferio/FramebufferDisplay.c +#: shared-bindings/is31fl3741/is31fl3741.c #: shared-bindings/rgbmatrix/RGBMatrix.c msgid "Brightness must be 0-1.0" msgstr "" @@ -1461,6 +1462,10 @@ msgstr "" msgid "Key must be 16, 24, or 32 bytes long" msgstr "" +#: shared-module/is31fl3741/is31fl3741.c +msgid "LED mappings must match display size" +msgstr "" + #: py/compile.c msgid "LHS of keyword arg must be an id" msgstr "" @@ -2085,6 +2090,10 @@ msgstr "" msgid "Sample rate too high. It must be less than %d" msgstr "" +#: shared-bindings/is31fl3741/is31fl3741.c +msgid "Scale dimensions must divide by 3" +msgstr "" + #: ports/nrf/common-hal/_bleio/Adapter.c msgid "Scan already in progess. Stop with stop_scan." msgstr "" @@ -2342,7 +2351,7 @@ msgstr "" msgid "Unable to create lock" msgstr "" -#: shared-module/displayio/I2CDisplay.c +#: shared-module/displayio/I2CDisplay.c shared-module/is31fl3741/is31fl3741.c #, c-format msgid "Unable to find I2C Display at %x" msgstr "" @@ -4453,6 +4462,7 @@ msgstr "" msgid "width must be from 2 to 8 (inclusive), not %d" msgstr "" +#: shared-bindings/is31fl3741/is31fl3741.c #: shared-bindings/rgbmatrix/RGBMatrix.c msgid "width must be greater than zero" msgstr "" diff --git a/main.c b/main.c index 988253d0d3..413a4f5b12 100644 --- a/main.c +++ b/main.c @@ -678,7 +678,7 @@ STATIC void __attribute__ ((noinline)) run_boot_py(safe_mode_t safe_mode) { FATFS *fs = &vfs->fatfs; boot_output = NULL; - bool write_boot_output = (common_hal_mcu_processor_get_reset_reason() == RESET_REASON_POWER_ON); + bool write_boot_output = true; FIL boot_output_file; if (f_open(fs, &boot_output_file, CIRCUITPY_BOOT_OUTPUT_FILE, FA_READ) == FR_OK) { char *file_contents = m_new(char, boot_text.alloc); @@ -858,7 +858,11 @@ int __attribute__((used)) main(void) { serial_write_compressed(translate("soft reboot\n")); } first_run = false; - skip_repl = run_code_py(safe_mode); + if (pyexec_mode_kind == PYEXEC_MODE_FRIENDLY_REPL) { + skip_repl = run_code_py(safe_mode); + } else { + skip_repl = false; + } } else if (exit_code != 0) { break; } diff --git a/ports/nrf/boards/microbit_v2/mpconfigboard.mk b/ports/nrf/boards/microbit_v2/mpconfigboard.mk index a52aa08f9a..a893acec44 100644 --- a/ports/nrf/boards/microbit_v2/mpconfigboard.mk +++ b/ports/nrf/boards/microbit_v2/mpconfigboard.mk @@ -13,6 +13,7 @@ CIRCUITPY_BUILTINS_POW3=0 CIRCUITPY_BUSDEVICE = 0 CIRCUITPY_COUNTIO = 0 CIRCUITPY_DISPLAYIO = 0 +CIRCUITPY_IS31FL3741 = 0 CIRCUITPY_FRAMEBUFFERIO = 0 CIRCUITPY_FREQUENCYIO = 0 CIRCUITPY_I2CPERIPHERAL = 0 diff --git a/ports/nrf/boards/pca10100/mpconfigboard.mk b/ports/nrf/boards/pca10100/mpconfigboard.mk index 59f2b217c3..8ae25393f8 100644 --- a/ports/nrf/boards/pca10100/mpconfigboard.mk +++ b/ports/nrf/boards/pca10100/mpconfigboard.mk @@ -19,6 +19,7 @@ CIRCUITPY_DISPLAYIO = 0 CIRCUITPY_FRAMEBUFFERIO = 0 CIRCUITPY_FREQUENCYIO = 0 CIRCUITPY_I2CPERIPHERAL = 0 +CIRCUITPY_IS31FL3741 = 0 CIRCUITPY_JSON = 0 CIRCUITPY_KEYPAD = 0 CIRCUITPY_MSGPACK = 0 diff --git a/ports/nrf/boards/simmel/mpconfigboard.mk b/ports/nrf/boards/simmel/mpconfigboard.mk index 67f5dfbd6d..85bdca6dc1 100644 --- a/ports/nrf/boards/simmel/mpconfigboard.mk +++ b/ports/nrf/boards/simmel/mpconfigboard.mk @@ -22,6 +22,7 @@ CIRCUITPY_DISPLAYIO = 0 CIRCUITPY_ERRNO = 0 CIRCUITPY_FRAMEBUFFERIO = 0 CIRCUITPY_GETPASS = 0 +CIRCUITPY_IS31FL3741 = 0 CIRCUITPY_KEYPAD = 0 CIRCUITPY_MSGPACK = 0 CIRCUITPY_NEOPIXEL_WRITE = 0 diff --git a/ports/nrf/mpconfigport.mk b/ports/nrf/mpconfigport.mk index 87d79cb0e5..55ade5562f 100644 --- a/ports/nrf/mpconfigport.mk +++ b/ports/nrf/mpconfigport.mk @@ -32,6 +32,8 @@ CIRCUITPY_BLEIO ?= 1 # No I2CPeripheral implementation CIRCUITPY_I2CPERIPHERAL = 0 +CIRCUITPY_IS31FL3741 ?= 1 + CIRCUITPY_RTC ?= 1 # frequencyio not yet implemented diff --git a/py/circuitpy_defns.mk b/py/circuitpy_defns.mk index 244abe9cba..e883481675 100644 --- a/py/circuitpy_defns.mk +++ b/py/circuitpy_defns.mk @@ -212,6 +212,9 @@ endif ifeq ($(CIRCUITPY_IPADDRESS),1) SRC_PATTERNS += ipaddress/% endif +ifeq ($(CIRCUITPY_IS31FL3741),1) +SRC_PATTERNS += is31fl3741/% +endif ifeq ($(CIRCUITPY_KEYPAD),1) SRC_PATTERNS += keypad/% endif @@ -551,6 +554,8 @@ SRC_SHARED_MODULE_ALL = \ imagecapture/ParallelImageCapture.c \ ipaddress/IPv4Address.c \ ipaddress/__init__.c \ + is31fl3741/IS31FL3741.c \ + is31fl3741/__init__.c \ keypad/__init__.c \ keypad/Event.c \ keypad/EventQueue.c \ diff --git a/py/circuitpy_mpconfig.mk b/py/circuitpy_mpconfig.mk index 28469f17f2..7f0721264b 100644 --- a/py/circuitpy_mpconfig.mk +++ b/py/circuitpy_mpconfig.mk @@ -247,6 +247,9 @@ CFLAGS += -DCIRCUITPY_IMAGECAPTURE=$(CIRCUITPY_IMAGECAPTURE) CIRCUITPY_IPADDRESS ?= $(CIRCUITPY_WIFI) CFLAGS += -DCIRCUITPY_IPADDRESS=$(CIRCUITPY_IPADDRESS) +CIRCUITPY_IS31FL3741 ?= 0 +CFLAGS += -DCIRCUITPY_IS31FL3741=$(CIRCUITPY_IS31FL3741) + CIRCUITPY_JSON ?= $(CIRCUITPY_FULL_BUILD) CFLAGS += -DCIRCUITPY_JSON=$(CIRCUITPY_JSON) diff --git a/shared-bindings/bitmaptools/__init__.c b/shared-bindings/bitmaptools/__init__.c index 57ac779081..ec26eab98e 100644 --- a/shared-bindings/bitmaptools/__init__.c +++ b/shared-bindings/bitmaptools/__init__.c @@ -664,7 +664,7 @@ MP_DEFINE_CONST_FUN_OBJ_KW(bitmaptools_readinto_obj, 0, bitmaptools_readinto); //| """The Floyd-Stenberg dither""" //| MAKE_ENUM_VALUE(bitmaptools_dither_algorithm_type, dither_algorithm, Atkinson, DITHER_ALGORITHM_ATKINSON); -MAKE_ENUM_VALUE(bitmaptools_dither_algorithm_type, dither_algorithm, FloydStenberg, DITHER_ALGORITHM_ATKINSON); +MAKE_ENUM_VALUE(bitmaptools_dither_algorithm_type, dither_algorithm, FloydStenberg, DITHER_ALGORITHM_FLOYD_STENBERG); MAKE_ENUM_MAP(bitmaptools_dither_algorithm) { MAKE_ENUM_MAP_ENTRY(dither_algorithm, Atkinson), diff --git a/shared-bindings/is31fl3741/IS31FL3741.c b/shared-bindings/is31fl3741/IS31FL3741.c new file mode 100644 index 0000000000..758dd114a0 --- /dev/null +++ b/shared-bindings/is31fl3741/IS31FL3741.c @@ -0,0 +1,308 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2021 Mark Komus + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "py/obj.h" +#include "py/objproperty.h" +#include "py/runtime.h" +#include "py/objarray.h" + +#include "shared-bindings/is31fl3741/IS31FL3741.h" +#include "shared-bindings/util.h" +#include "shared-module/displayio/__init__.h" +#include "shared-module/framebufferio/__init__.h" +#include "shared-module/framebufferio/FramebufferDisplay.h" +#include "shared-bindings/busio/I2C.h" + +//| class IS31FL3741: +//| """Displays an in-memory framebuffer to a IS31FL3741 drive display.""" +//| + +//| def __init__(self, *, width: int) -> None: +//| """Create a IS31FL3741 object with the given attributes. +//| +//| The framebuffer is in "RGB888" format using 4 bytes per pixel. +//| Bits 24-31 are ignored. The format is in RGB order. +//| +//| If a framebuffer is not passed in, one is allocated and initialized +//| to all black. In any case, the framebuffer can be retrieved +//| by passing the Is31fl3741 object to memoryview(). +//| +//| A Is31fl3741 is often used in conjunction with a +//| `framebufferio.FramebufferDisplay`.""" +//| + +STATIC mp_obj_t is31fl3741_IS31FL3741_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) { + enum { ARG_width, ARG_height, ARG_i2c, ARG_addr, ARG_framebuffer, ARG_mapping, ARG_scale, ARG_gamma }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_width, MP_ARG_INT | MP_ARG_REQUIRED | MP_ARG_KW_ONLY }, + { MP_QSTR_height, MP_ARG_INT | MP_ARG_REQUIRED | MP_ARG_KW_ONLY }, + { MP_QSTR_i2c, MP_ARG_OBJ | MP_ARG_REQUIRED | MP_ARG_KW_ONLY }, + { MP_QSTR_addr, MP_ARG_INT | MP_ARG_KW_ONLY, { .u_int = 0x30 } }, + { MP_QSTR_framebuffer, MP_ARG_OBJ | MP_ARG_KW_ONLY, { .u_obj = mp_const_none } }, + { MP_QSTR_mapping, MP_ARG_OBJ | MP_ARG_KW_ONLY | MP_ARG_REQUIRED }, + { MP_QSTR_scale, MP_ARG_BOOL | MP_ARG_KW_ONLY, { .u_bool = false } }, + { MP_QSTR_gamma, MP_ARG_BOOL | MP_ARG_KW_ONLY, { .u_bool = false } }, + }; + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all_kw_array(n_args, n_kw, all_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + + mp_obj_t i2c = mp_arg_validate_type(args[ARG_i2c].u_obj, &busio_i2c_type, MP_QSTR_i2c_bus); + + is31fl3741_IS31FL3741_obj_t *self = &allocate_display_bus_or_raise()->is31fl3741; + self->base.type = &is31fl3741_IS31FL3741_type; + + if (args[ARG_width].u_int <= 0) { + mp_raise_ValueError(translate("width must be greater than zero")); + } + + self->scale = args[ARG_scale].u_bool; + if (self->scale) { + if (((args[ARG_height].u_int % 3) != 0) || ((args[ARG_width].u_int % 3) != 0)) { + mp_raise_ValueError(translate("Scale dimensions must divide by 3")); + } + + self->scale_width = args[ARG_width].u_int / 3; + self->scale_height = args[ARG_height].u_int / 3; + } else { + self->scale_width = args[ARG_width].u_int; + self->scale_height = args[ARG_height].u_int; + } + + self->auto_gamma = args[ARG_gamma].u_bool; + + mp_obj_t framebuffer = args[ARG_framebuffer].u_obj; + if (framebuffer == mp_const_none) { + int width = args[ARG_width].u_int; + int height = args[ARG_height].u_int; + int bufsize = 4 * width * height; + framebuffer = mp_obj_new_bytearray_of_zeros(bufsize); + } + + common_hal_is31fl3741_IS31FL3741_construct(self, + args[ARG_width].u_int, + args[ARG_height].u_int, + framebuffer, + MP_OBJ_TO_PTR(i2c), + args[ARG_addr].u_int, + args[ARG_mapping].u_obj + ); + + return MP_OBJ_FROM_PTR(self); +} + +//| def deinit(self) -> None: +//| """Free the resources associated with this +//| IS31FL3741 instance. After deinitialization, no further operations +//| may be performed.""" +//| ... +//| +STATIC mp_obj_t is31fl3741_IS31FL3741_deinit(mp_obj_t self_in) { + is31fl3741_IS31FL3741_obj_t *self = (is31fl3741_IS31FL3741_obj_t *)self_in; + common_hal_is31fl3741_IS31FL3741_deinit(self); + return mp_const_none; +} + +STATIC MP_DEFINE_CONST_FUN_OBJ_1(is31fl3741_IS31FL3741_deinit_obj, is31fl3741_IS31FL3741_deinit); + +static void check_for_deinit(is31fl3741_IS31FL3741_obj_t *self) { + if (self->framebuffer == NULL) { + raise_deinited_error(); + } +} + +//| brightness: float +//| """In the current implementation, 0.0 turns the display off entirely +//| and any other value up to 1.0 turns the display on fully.""" +//| +STATIC mp_obj_t is31fl3741_IS31FL3741_get_brightness(mp_obj_t self_in) { + is31fl3741_IS31FL3741_obj_t *self = (is31fl3741_IS31FL3741_obj_t *)self_in; + check_for_deinit(self); + uint8_t current = common_hal_is31fl3741_IS31FL3741_get_global_current(self); + + float brightness = (float)current / (float)0xFF; + return mp_obj_new_float(brightness); +} +MP_DEFINE_CONST_FUN_OBJ_1(is31fl3741_IS31FL3741_get_brightness_obj, is31fl3741_IS31FL3741_get_brightness); + +STATIC mp_obj_t is31fl3741_IS31FL3741_set_brightness(mp_obj_t self_in, mp_obj_t value_in) { + is31fl3741_IS31FL3741_obj_t *self = (is31fl3741_IS31FL3741_obj_t *)self_in; + check_for_deinit(self); + mp_float_t brightness = mp_obj_get_float(value_in); + if (brightness < 0.0f || brightness > 1.0f) { + mp_raise_ValueError(translate("Brightness must be 0-1.0")); + } + + uint8_t current = (uint8_t)(brightness * 0xFF); + common_hal_is31fl3741_IS31FL3741_set_global_current(self, current); + + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_2(is31fl3741_IS31FL3741_set_brightness_obj, is31fl3741_IS31FL3741_set_brightness); + +const mp_obj_property_t is31fl3741_IS31FL3741_brightness_obj = { + .base.type = &mp_type_property, + .proxy = {(mp_obj_t)&is31fl3741_IS31FL3741_get_brightness_obj, + (mp_obj_t)&is31fl3741_IS31FL3741_set_brightness_obj, + MP_ROM_NONE}, +}; + +//| def refresh(self) -> None: +//| """Transmits the color data in the buffer to the pixels so that +//| they are shown.""" +//| ... +//| +STATIC mp_obj_t is31fl3741_IS31FL3741_refresh(mp_obj_t self_in) { + is31fl3741_IS31FL3741_obj_t *self = (is31fl3741_IS31FL3741_obj_t *)self_in; + check_for_deinit(self); + common_hal_is31fl3741_IS31FL3741_refresh(self, 0); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_1(is31fl3741_IS31FL3741_refresh_obj, is31fl3741_IS31FL3741_refresh); + +//| width: int +//| """The width of the display, in pixels""" +//| +STATIC mp_obj_t is31fl3741_IS31FL3741_get_width(mp_obj_t self_in) { + is31fl3741_IS31FL3741_obj_t *self = (is31fl3741_IS31FL3741_obj_t *)self_in; + check_for_deinit(self); + return MP_OBJ_NEW_SMALL_INT(common_hal_is31fl3741_IS31FL3741_get_width(self)); +} +MP_DEFINE_CONST_FUN_OBJ_1(is31fl3741_IS31FL3741_get_width_obj, is31fl3741_IS31FL3741_get_width); +const mp_obj_property_t is31fl3741_IS31FL3741_width_obj = { + .base.type = &mp_type_property, + .proxy = {(mp_obj_t)&is31fl3741_IS31FL3741_get_width_obj, + MP_ROM_NONE, + MP_ROM_NONE}, +}; + +//| height: int +//| """The height of the display, in pixels""" +//| +STATIC mp_obj_t is31fl3741_IS31FL3741_get_height(mp_obj_t self_in) { + is31fl3741_IS31FL3741_obj_t *self = (is31fl3741_IS31FL3741_obj_t *)self_in; + check_for_deinit(self); + return MP_OBJ_NEW_SMALL_INT(common_hal_is31fl3741_IS31FL3741_get_height(self)); +} +MP_DEFINE_CONST_FUN_OBJ_1(is31fl3741_IS31FL3741_get_height_obj, is31fl3741_IS31FL3741_get_height); +const mp_obj_property_t is31fl3741_IS31FL3741_height_obj = { + .base.type = &mp_type_property, + .proxy = {(mp_obj_t)&is31fl3741_IS31FL3741_get_height_obj, + MP_ROM_NONE, + MP_ROM_NONE}, +}; + +STATIC const mp_rom_map_elem_t is31fl3741_IS31FL3741_locals_dict_table[] = { + { MP_ROM_QSTR(MP_QSTR_deinit), MP_ROM_PTR(&is31fl3741_IS31FL3741_deinit_obj) }, + { MP_ROM_QSTR(MP_QSTR_brightness), MP_ROM_PTR(&is31fl3741_IS31FL3741_brightness_obj) }, + { MP_ROM_QSTR(MP_QSTR_refresh), MP_ROM_PTR(&is31fl3741_IS31FL3741_refresh_obj) }, + { MP_ROM_QSTR(MP_QSTR_width), MP_ROM_PTR(&is31fl3741_IS31FL3741_width_obj) }, + { MP_ROM_QSTR(MP_QSTR_height), MP_ROM_PTR(&is31fl3741_IS31FL3741_height_obj) }, +}; +STATIC MP_DEFINE_CONST_DICT(is31fl3741_IS31FL3741_locals_dict, is31fl3741_IS31FL3741_locals_dict_table); + +STATIC void is31fl3741_IS31FL3741_get_bufinfo(mp_obj_t self_in, mp_buffer_info_t *bufinfo) { + is31fl3741_IS31FL3741_obj_t *self = (is31fl3741_IS31FL3741_obj_t *)self_in; + check_for_deinit(self); + + *bufinfo = self->bufinfo; +} + +STATIC void is31fl3741_IS31FL3741_swapbuffers(mp_obj_t self_in, uint8_t *dirty_row_bitmap) { + common_hal_is31fl3741_IS31FL3741_refresh(self_in, dirty_row_bitmap); +} + +STATIC void is31fl3741_IS31FL3741_deinit_proto(mp_obj_t self_in) { + common_hal_is31fl3741_IS31FL3741_deinit(self_in); +} + +STATIC float is31fl3741_IS31FL3741_get_brightness_proto(mp_obj_t self_in) { + return common_hal_is31fl3741_IS31FL3741_get_paused(self_in) ? 0.0f : 1.0f; +} + +STATIC bool is31fl3741_IS31FL3741_set_brightness_proto(mp_obj_t self_in, mp_float_t value) { + common_hal_is31fl3741_IS31FL3741_set_paused(self_in, value <= 0); + return true; +} + +STATIC int is31fl3741_IS31FL3741_get_width_proto(mp_obj_t self_in) { + return common_hal_is31fl3741_IS31FL3741_get_width(self_in); +} + +STATIC int is31fl3741_IS31FL3741_get_height_proto(mp_obj_t self_in) { + return common_hal_is31fl3741_IS31FL3741_get_height(self_in); +} + +STATIC int is31fl3741_IS31FL3741_get_color_depth_proto(mp_obj_t self_in) { + // The way displayio works depth is used to calculate bytes + // We use an uint32_t for color already so setting to 24 causes + // more changes required + return 32; +} + +STATIC int is31fl3741_IS31FL3741_get_bytes_per_cell_proto(mp_obj_t self_in) { + return 1; +} + +STATIC int is31fl3741_IS31FL3741_get_native_frames_per_second_proto(mp_obj_t self_in) { + return 60; // This was just chosen may vary based on LEDs used? +} + +STATIC const framebuffer_p_t is31fl3741_IS31FL3741_proto = { + MP_PROTO_IMPLEMENT(MP_QSTR_protocol_framebuffer) + .get_bufinfo = is31fl3741_IS31FL3741_get_bufinfo, + .set_brightness = is31fl3741_IS31FL3741_set_brightness_proto, + .get_brightness = is31fl3741_IS31FL3741_get_brightness_proto, + .get_width = is31fl3741_IS31FL3741_get_width_proto, + .get_height = is31fl3741_IS31FL3741_get_height_proto, + .get_color_depth = is31fl3741_IS31FL3741_get_color_depth_proto, + .get_bytes_per_cell = is31fl3741_IS31FL3741_get_bytes_per_cell_proto, + .get_native_frames_per_second = is31fl3741_IS31FL3741_get_native_frames_per_second_proto, + .swapbuffers = is31fl3741_IS31FL3741_swapbuffers, + .deinit = is31fl3741_IS31FL3741_deinit_proto, +}; + +STATIC mp_int_t is31fl3741_IS31FL3741_get_buffer(mp_obj_t self_in, mp_buffer_info_t *bufinfo, mp_uint_t flags) { + is31fl3741_IS31FL3741_obj_t *self = (is31fl3741_IS31FL3741_obj_t *)self_in; + // a readonly framebuffer would be unusual but not impossible + if ((flags & MP_BUFFER_WRITE) && !(self->bufinfo.typecode & MP_OBJ_ARRAY_TYPECODE_FLAG_RW)) { + return 1; + } + *bufinfo = self->bufinfo; + bufinfo->typecode = 'H'; + return 0; +} + +const mp_obj_type_t is31fl3741_IS31FL3741_type = { + { &mp_type_type }, + .flags = MP_TYPE_FLAG_EXTENDED, + .name = MP_QSTR_is31fl3741, + .locals_dict = (mp_obj_dict_t *)&is31fl3741_IS31FL3741_locals_dict, + .make_new = is31fl3741_IS31FL3741_make_new, + MP_TYPE_EXTENDED_FIELDS( + .buffer_p = { .get_buffer = is31fl3741_IS31FL3741_get_buffer, }, + .protocol = &is31fl3741_IS31FL3741_proto, + ), +}; diff --git a/shared-bindings/is31fl3741/IS31FL3741.h b/shared-bindings/is31fl3741/IS31FL3741.h new file mode 100644 index 0000000000..3815f10de0 --- /dev/null +++ b/shared-bindings/is31fl3741/IS31FL3741.h @@ -0,0 +1,61 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2021 Mark Komus + * + * 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 "shared-module/is31fl3741/IS31FL3741.h" + +extern const mp_obj_type_t is31fl3741_IS31FL3741_type; + +void common_hal_is31fl3741_IS31FL3741_construct(is31fl3741_IS31FL3741_obj_t *self, int width, int height, mp_obj_t framebuffer, busio_i2c_obj_t *i2c, uint8_t addr, mp_obj_t mapping); + +void common_hal_is31fl3741_IS31FL3741_deinit(is31fl3741_IS31FL3741_obj_t *); + +int common_hal_is31fl3741_IS31FL3741_get_width(is31fl3741_IS31FL3741_obj_t *self); +int common_hal_is31fl3741_IS31FL3741_get_height(is31fl3741_IS31FL3741_obj_t *self); + +void common_hal_displayio_is31fl3741_begin_transaction(is31fl3741_IS31FL3741_obj_t *self); +void common_hal_displayio_is31fl3741_end_transaction(is31fl3741_IS31FL3741_obj_t *self); + +void common_hal_is31fl3741_IS31FL3741_set_global_current(is31fl3741_IS31FL3741_obj_t *self, uint8_t current); +uint8_t common_hal_is31fl3741_IS31FL3741_get_global_current(is31fl3741_IS31FL3741_obj_t *self); + +void common_hal_is31fl3741_IS31FL3741_set_paused(is31fl3741_IS31FL3741_obj_t *self, bool paused); +bool common_hal_is31fl3741_IS31FL3741_get_paused(is31fl3741_IS31FL3741_obj_t *self); +void common_hal_is31fl3741_IS31FL3741_refresh(is31fl3741_IS31FL3741_obj_t *self, uint8_t *dirtyrows); + +void common_hal_is31fl3741_IS31FL3741_reconstruct(is31fl3741_IS31FL3741_obj_t *self, mp_obj_t framebuffer); + +void is31fl3741_IS31FL3741_collect_ptrs(is31fl3741_IS31FL3741_obj_t *self); + +void is31fl3741_send_unlock(busio_i2c_obj_t *i2c, uint8_t addr); +void is31fl3741_set_page(busio_i2c_obj_t *i2c, uint8_t addr, uint8_t p); +void is31fl3741_send_enable(busio_i2c_obj_t *i2c, uint8_t addr); +void is31fl3741_send_reset(busio_i2c_obj_t *i2c, uint8_t addr); +void is31fl3741_set_current(busio_i2c_obj_t *i2c, uint8_t addr, uint8_t current); +uint8_t is31fl3741_get_current(busio_i2c_obj_t *i2c, uint8_t addr); +void is31fl3741_set_led(busio_i2c_obj_t *i2c, uint8_t addr, uint16_t led, uint8_t level, uint8_t page); +void is31fl3741_draw_pixel(busio_i2c_obj_t *i2c, uint8_t addr, int16_t x, int16_t y, uint32_t color, uint16_t *mapping); diff --git a/shared-bindings/is31fl3741/__init__.c b/shared-bindings/is31fl3741/__init__.c new file mode 100644 index 0000000000..90b9426a4d --- /dev/null +++ b/shared-bindings/is31fl3741/__init__.c @@ -0,0 +1,46 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2021 Mark Komus + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include + +#include "py/obj.h" +#include "py/runtime.h" + +#include "shared-bindings/is31fl3741/IS31FL3741.h" + +STATIC const mp_rom_map_elem_t is31fl3741_module_globals_table[] = { + { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_is31fl3741) }, + { MP_ROM_QSTR(MP_QSTR_IS31FL3741), MP_ROM_PTR(&is31fl3741_IS31FL3741_type) }, +}; + +STATIC MP_DEFINE_CONST_DICT(is31fl3741_module_globals, is31fl3741_module_globals_table); + +const mp_obj_module_t is31fl3741_module = { + .base = { &mp_type_module }, + .globals = (mp_obj_dict_t *)&is31fl3741_module_globals, +}; + +MP_REGISTER_MODULE(MP_QSTR_is31fl3741, is31fl3741_module, CIRCUITPY_IS31FL3741); diff --git a/shared-module/bitmaptools/__init__.c b/shared-module/bitmaptools/__init__.c index 80b4a38964..6b5def2904 100644 --- a/shared-module/bitmaptools/__init__.c +++ b/shared-module/bitmaptools/__init__.c @@ -749,9 +749,9 @@ void common_hal_bitmaptools_dither(displayio_bitmap_t *dest_bitmap, displayio_bi int x1 = x + info->terms[i].dx; int dy = info->terms[i].dy; - rows[dy][x1] = ((info->terms[i].dl * err) >> 8) + rows[dy][x1]; + rows[dy][x1] = ((info->terms[i].dl * err) / 256) + rows[dy][x1]; } - err = err * info->dl >> 8; + err = (err * info->dl) / 256; } write_pixels(dest_bitmap, y, out); @@ -761,13 +761,13 @@ void common_hal_bitmaptools_dither(displayio_bitmap_t *dest_bitmap, displayio_bi rows[1] = rows[2]; rows[2] = tmp; - fill_row(source_bitmap, swap, rows[2], y + 2, info->mx); - y++; if (y == height) { break; } + fill_row(source_bitmap, swap, rows[2], y + 2, info->mx); + // Serpentine dither. Going right-to-left... for (int x = width; x--;) { int16_t pixel_in = rows[0][x] + err; @@ -779,9 +779,9 @@ void common_hal_bitmaptools_dither(displayio_bitmap_t *dest_bitmap, displayio_bi int x1 = x - info->terms[i].dx; int dy = info->terms[i].dy; - rows[dy][x1] = ((info->terms[i].dl * err) >> 8) + rows[dy][x1]; + rows[dy][x1] = ((info->terms[i].dl * err) / 256) + rows[dy][x1]; } - err = err * info->dl >> 8; + err = (err * info->dl) / 256; } write_pixels(dest_bitmap, y, out); @@ -790,7 +790,7 @@ void common_hal_bitmaptools_dither(displayio_bitmap_t *dest_bitmap, displayio_bi rows[1] = rows[2]; rows[2] = tmp; - fill_row(source_bitmap, swap, rows[2], y + 2, info->mx); + fill_row(source_bitmap, swap, rows[2], y + 3, info->mx); } displayio_area_t a = { 0, 0, width, height, NULL }; diff --git a/shared-module/displayio/Palette.c b/shared-module/displayio/Palette.c index 0325aaab2c..1bd168b354 100644 --- a/shared-module/displayio/Palette.c +++ b/shared-module/displayio/Palette.c @@ -98,6 +98,8 @@ bool displayio_palette_get_color(displayio_palette_t *self, const _displayio_col *color = packed; } else if (colorspace->depth == 32) { *color = self->colors[palette_index].rgb888; + } else { + return false; } return true; diff --git a/shared-module/displayio/TileGrid.c b/shared-module/displayio/TileGrid.c index ff4f56efc4..dbf2ce545b 100644 --- a/shared-module/displayio/TileGrid.c +++ b/shared-module/displayio/TileGrid.c @@ -474,7 +474,7 @@ bool displayio_tilegrid_fill_area(displayio_tilegrid_t *self, if (colorspace->depth == 16) { *(((uint16_t *)buffer) + offset) = output_pixel.pixel; } else if (colorspace->depth == 32) { - buffer[offset] = output_pixel.pixel; + *(((uint32_t *)buffer) + offset) = output_pixel.pixel; } else if (colorspace->depth == 8) { *(((uint8_t *)buffer) + offset) = output_pixel.pixel; } else if (colorspace->depth < 8) { diff --git a/shared-module/displayio/__init__.c b/shared-module/displayio/__init__.c index 357dc64179..ff085d45ef 100644 --- a/shared-module/displayio/__init__.c +++ b/shared-module/displayio/__init__.c @@ -65,8 +65,7 @@ displayio_buffer_transform_t null_transform = { .transpose_xy = false }; - -#if CIRCUITPY_RGBMATRIX || CIRCUITPY_VIDEOCORE +#if CIRCUITPY_RGBMATRIX || CIRCUITPY_IS31FL3741 || CIRCUITPY_VIDEOCORE 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].display_base.type == &framebufferio_framebufferdisplay_type) { @@ -143,6 +142,10 @@ void common_hal_displayio_release_displays(void) { } else if (bus_type == &rgbmatrix_RGBMatrix_type) { common_hal_rgbmatrix_rgbmatrix_deinit(&displays[i].rgbmatrix); #endif + #if CIRCUITPY_IS31FL3741 + } else if (bus_type == &is31fl3741_IS31FL3741_type) { + common_hal_is31fl3741_IS31FL3741_deinit(&displays[i].is31fl3741); + #endif #if CIRCUITPY_SHARPDISPLAY } else if (displays[i].bus_base.type == &sharpdisplay_framebuffer_type) { common_hal_sharpdisplay_framebuffer_deinit(&displays[i].sharpdisplay); @@ -221,6 +224,37 @@ void reset_displays(void) { common_hal_rgbmatrix_rgbmatrix_set_paused(pm, true); } #endif + #if CIRCUITPY_IS31FL3741 + } else if (displays[i].is31fl3741.base.type == &is31fl3741_IS31FL3741_type) { + is31fl3741_IS31FL3741_obj_t *is31 = &displays[i].is31fl3741; + if (((uint32_t)is31->i2c) < ((uint32_t)&displays) || + ((uint32_t)is31->i2c) > ((uint32_t)&displays + CIRCUITPY_DISPLAY_LIMIT)) { + busio_i2c_obj_t *original_i2c = is31->i2c; + #if BOARD_I2C + // We don't need to move original_i2c if it is the board.I2C object because it is + // statically allocated already. (Doing so would also make it impossible to reference in + // a subsequent VM run.) + if (original_i2c == common_hal_board_get_i2c()) { + continue; + } + #endif + memcpy(&is31->inline_i2c, original_i2c, sizeof(busio_i2c_obj_t)); + is31->i2c = &is31->inline_i2c; + // Check for other displays that use the same i2c bus and swap them too. + /*for (uint8_t j = i + 1; j < CIRCUITPY_DISPLAY_LIMIT; j++) { + if (displays[i].i2cdisplay_bus.base.type == &displayio_i2cdisplay_type && + displays[i].i2cdisplay_bus.bus == original_i2c) { + displays[i].i2cdisplay_bus.bus = &i2c->inline_bus; + } + }*/ + } + + if (!any_display_uses_this_framebuffer(&is31->base)) { + common_hal_is31fl3741_IS31FL3741_deinit(is31); + } else { + common_hal_is31fl3741_IS31FL3741_set_paused(is31, true); + } + #endif #if CIRCUITPY_SHARPDISPLAY } else if (displays[i].bus_base.type == &sharpdisplay_framebuffer_type) { sharpdisplay_framebuffer_obj_t *sharp = &displays[i].sharpdisplay; @@ -264,6 +298,11 @@ void displayio_gc_collect(void) { rgbmatrix_rgbmatrix_collect_ptrs(&displays[i].rgbmatrix); } #endif + #if CIRCUITPY_IS31FL3741 + if (displays[i].is31fl3741.base.type == &is31fl3741_IS31FL3741_type) { + is31fl3741_IS31FL3741_collect_ptrs(&displays[i].is31fl3741); + } + #endif #if CIRCUITPY_SHARPDISPLAY if (displays[i].bus_base.type == &sharpdisplay_framebuffer_type) { common_hal_sharpdisplay_framebuffer_collect_ptrs(&displays[i].sharpdisplay); diff --git a/shared-module/displayio/__init__.h b/shared-module/displayio/__init__.h index ad397c8254..b4133308eb 100644 --- a/shared-module/displayio/__init__.h +++ b/shared-module/displayio/__init__.h @@ -41,6 +41,9 @@ #if CIRCUITPY_RGBMATRIX #include "shared-bindings/rgbmatrix/RGBMatrix.h" #endif +#if CIRCUITPY_IS31FL3741 +#include "shared-bindings/is31fl3741/IS31FL3741.h" +#endif #if CIRCUITPY_SHARPDISPLAY #include "shared-module/sharpdisplay/SharpMemoryFramebuffer.h" #endif @@ -59,6 +62,9 @@ typedef struct { #if CIRCUITPY_RGBMATRIX rgbmatrix_rgbmatrix_obj_t rgbmatrix; #endif + #if CIRCUITPY_IS31FL3741 + is31fl3741_IS31FL3741_obj_t is31fl3741; + #endif #if CIRCUITPY_SHARPDISPLAY sharpdisplay_framebuffer_obj_t sharpdisplay; #endif diff --git a/shared-module/is31fl3741/IS31FL3741.c b/shared-module/is31fl3741/IS31FL3741.c new file mode 100644 index 0000000000..9d7316e1cd --- /dev/null +++ b/shared-module/is31fl3741/IS31FL3741.c @@ -0,0 +1,353 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2021 Mark Komus + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include + +#include "py/gc.h" +#include "py/obj.h" +#include "py/objarray.h" +#include "py/objproperty.h" +#include "py/runtime.h" + +#include "shared-module/is31fl3741/allocator.h" +#include "shared-bindings/is31fl3741/IS31FL3741.h" +#include "shared-bindings/microcontroller/Pin.h" +#include "shared-bindings/microcontroller/__init__.h" +#include "shared-bindings/util.h" +#include "shared-module/framebufferio/FramebufferDisplay.h" +#include "shared-bindings/busio/I2C.h" + +void common_hal_is31fl3741_IS31FL3741_construct(is31fl3741_IS31FL3741_obj_t *self, int width, int height, mp_obj_t framebuffer, busio_i2c_obj_t *i2c, uint8_t addr, mp_obj_t mapping) { + self->width = width; + self->height = height; + + self->bufsize = sizeof(uint32_t) * width * height; + + // Probe the bus to see if a device acknowledges the given address. + if (!common_hal_busio_i2c_probe(i2c, addr)) { + self->base.type = &mp_type_NoneType; + mp_raise_ValueError_varg(translate("Unable to find I2C Display at %x"), addr); + } + + self->i2c = i2c; + self->device_address = addr; + common_hal_busio_i2c_never_reset(self->i2c); + // Our object is statically allocated off the heap so make sure the bus object lives to the end + // of the heap as well. + gc_never_free(self->i2c); + + mp_obj_t *items; + size_t len; + mp_obj_list_get(mapping, &len, &items); + + if (len != (size_t)(self->scale_width * self->scale_height * 3)) { + mp_raise_ValueError(translate("LED mappings must match display size")); + } + + self->mapping = common_hal_is31fl3741_allocator_impl(sizeof(uint16_t) * len); + for (size_t i = 0; i < len; i++) { + mp_int_t value = mp_obj_get_int(items[i]); + // We only store up to 16 bits + if (value > 0xFFFF) { + value = 0xFFFF; + } + self->mapping[i] = (uint16_t)value; + } + + common_hal_is31fl3741_IS31FL3741_reconstruct(self, framebuffer); +} + +void common_hal_is31fl3741_IS31FL3741_reconstruct(is31fl3741_IS31FL3741_obj_t *self, mp_obj_t framebuffer) { + self->paused = 1; + + if (framebuffer) { + self->framebuffer = framebuffer; + 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; + } else { + self->bufinfo.typecode = 'H'; + } + + // 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 { + common_hal_is31fl3741_free_impl(self->bufinfo.buf); + + self->framebuffer = NULL; + self->bufinfo.buf = common_hal_is31fl3741_allocator_impl(self->bufsize); + self->bufinfo.len = self->bufsize; + self->bufinfo.typecode = 'H' | MP_OBJ_ARRAY_TYPECODE_FLAG_RW; + } + + common_hal_displayio_is31fl3741_begin_transaction(self); + + uint8_t command = 0xFC; // device ID + common_hal_busio_i2c_write(self->i2c, self->device_address, &command, 1, false); + uint8_t data = 0; + common_hal_busio_i2c_read(self->i2c, self->device_address, &data, 1); + + is31fl3741_send_reset(self->i2c, self->device_address); + is31fl3741_send_enable(self->i2c, self->device_address); + is31fl3741_set_current(self->i2c, self->device_address, 0xFF); + + // set scale (brightness) to max for all LEDs + for (int i; i < 351; i++) { + is31fl3741_set_led(self->i2c, self->device_address, i, 0xFF, 2); + } + + common_hal_displayio_is31fl3741_end_transaction(self); + + self->paused = 0; +} + +void common_hal_is31fl3741_IS31FL3741_deinit(is31fl3741_IS31FL3741_obj_t *self) { + common_hal_displayio_is31fl3741_end_transaction(self); // in case we still had a lock + + if (self->i2c == &self->inline_i2c) { + common_hal_busio_i2c_deinit(self->i2c); + self->i2c = NULL; + } + + if (self->mapping != 0) { + common_hal_is31fl3741_free_impl(self->mapping); + self->mapping = 0; + } + + self->base.type = NULL; + + // If a framebuffer was passed in to the constructor, NULL the reference + // here so that it will become GC'able + self->framebuffer = NULL; +} + +void common_hal_is31fl3741_IS31FL3741_set_paused(is31fl3741_IS31FL3741_obj_t *self, bool paused) { + self->paused = paused; +} + +bool common_hal_is31fl3741_IS31FL3741_get_paused(is31fl3741_IS31FL3741_obj_t *self) { + return self->paused; +} + +void common_hal_is31fl3741_IS31FL3741_set_global_current(is31fl3741_IS31FL3741_obj_t *self, uint8_t current) { + common_hal_displayio_is31fl3741_begin_transaction(self); + is31fl3741_set_current(self->i2c, self->device_address, current); + common_hal_displayio_is31fl3741_end_transaction(self); +} + +uint8_t common_hal_is31fl3741_IS31FL3741_get_global_current(is31fl3741_IS31FL3741_obj_t *self) { + common_hal_displayio_is31fl3741_begin_transaction(self); + uint8_t current = is31fl3741_get_current(self->i2c, self->device_address); + common_hal_displayio_is31fl3741_end_transaction(self); + return current; +} + +void common_hal_is31fl3741_IS31FL3741_refresh(is31fl3741_IS31FL3741_obj_t *self, uint8_t *dirtyrows) { + common_hal_displayio_is31fl3741_begin_transaction(self); + if (!self->paused) { + uint8_t dirty_row_flags = 0xFF; // only supports 8 rows gotta fix + + if (self->scale) { + // Based on the Arduino IS31FL3741 driver code + // dirtyrows flag current not implemented for scaled displays + uint32_t *buffer = self->bufinfo.buf; + + for (int x = 0; x < self->scale_width; x++) { + uint32_t *ptr = &buffer[x * 3]; // Entry along top scan line w/x offset + for (int y = 0; y < self->scale_height; y++) { + uint16_t rsum = 0, gsum = 0, bsum = 0; + // Inner x/y loops are row-major on purpose (less pointer math) + for (uint8_t yy = 0; yy < 3; yy++) { + for (uint8_t xx = 0; xx < 3; xx++) { + uint32_t rgb = ptr[xx]; + rsum += rgb >> 16 & 0xFF; + gsum += (rgb >> 8) & 0xFF; + bsum += rgb & 0xFF; + } + ptr += self->width; // Advance one scan line + } + rsum = rsum / 9; + gsum = gsum / 9; + bsum = bsum / 9; + uint32_t color = 0; + if (self->auto_gamma) { + color = (IS31GammaTable[rsum] << 16) + + (IS31GammaTable[gsum] << 8) + + IS31GammaTable[bsum]; + } else { + color = (rsum << 16) + (gsum << 8) + bsum; + } + is31fl3741_draw_pixel(self->i2c, self->device_address, x, y, color, self->mapping); + } + } + } else { + uint32_t *buffer = self->bufinfo.buf; + for (int y = 0; y < self->height; y++) { + if ((dirtyrows != 0) && ((y % 8) == 0)) { + dirty_row_flags = *dirtyrows++; + } + + if ((dirty_row_flags >> (y % 8)) & 0x1) { + uint32_t color = 0; + if (self->auto_gamma) { + color = IS31GammaTable[((*buffer) >> 16 & 0xFF)] + + IS31GammaTable[((*buffer) >> 8 & 0xFF)] + + IS31GammaTable[((*buffer) & 0xFF)]; + } else { + color = *buffer; + } + + for (int x = 0; x < self->width; x++) { + is31fl3741_draw_pixel(self->i2c, self->device_address, x, y, color, self->mapping); + buffer++; + } + } + } + } + } + + common_hal_displayio_is31fl3741_end_transaction(self); +} + +int common_hal_is31fl3741_IS31FL3741_get_width(is31fl3741_IS31FL3741_obj_t *self) { + return self->width; +} + +int common_hal_is31fl3741_IS31FL3741_get_height(is31fl3741_IS31FL3741_obj_t *self) { + return self->height; +} + +void common_hal_displayio_is31fl3741_begin_transaction(is31fl3741_IS31FL3741_obj_t *self) { + while (!common_hal_busio_i2c_try_lock(self->i2c)) { + RUN_BACKGROUND_TASKS; + if (mp_hal_is_interrupted()) { + break; + } + } +} + +void common_hal_displayio_is31fl3741_end_transaction(is31fl3741_IS31FL3741_obj_t *self) { + common_hal_busio_i2c_unlock(self->i2c); +} + +void *common_hal_is31fl3741_allocator_impl(size_t sz) { + supervisor_allocation *allocation = allocate_memory(align32_size(sz), false, true); + return allocation ? allocation->ptr : NULL; +} + +void common_hal_is31fl3741_free_impl(void *ptr_in) { + free_memory(allocation_from_ptr(ptr_in)); +} + +void is31fl3741_IS31FL3741_collect_ptrs(is31fl3741_IS31FL3741_obj_t *self) { + gc_collect_ptr(self->framebuffer); + gc_collect_ptr(self->mapping); +} + +// The following are routines to manipulate the IS31FL3741 chip +// They are not meant to be called by user code but only used +// internally. + +uint8_t cur_page = 99; // set to invalid page to start + +void is31fl3741_send_unlock(busio_i2c_obj_t *i2c, uint8_t addr) { + uint8_t unlock[2] = { 0xFE, 0xC5 }; // unlock command + common_hal_busio_i2c_write(i2c, addr, unlock, 2, true); +} + +void is31fl3741_set_page(busio_i2c_obj_t *i2c, uint8_t addr, uint8_t p) { + if (p == cur_page) { + return; + } + + cur_page = p; + is31fl3741_send_unlock(i2c, addr); + + uint8_t page[2] = { 0xFD, 0x00 }; // page command + page[1] = p; + common_hal_busio_i2c_write(i2c, addr, page, 2, true); +} + +void is31fl3741_send_enable(busio_i2c_obj_t *i2c, uint8_t addr) { + is31fl3741_set_page(i2c, addr, 4); + uint8_t enable[2] = { 0x00, 0x01 }; // enable command + common_hal_busio_i2c_write(i2c, addr, enable, 2, true); +} + +void is31fl3741_send_reset(busio_i2c_obj_t *i2c, uint8_t addr) { + is31fl3741_set_page(i2c, addr, 4); + uint8_t rst[2] = { 0x3F, 0xAE }; // reset command + common_hal_busio_i2c_write(i2c, addr, rst, 2, true); +} + +void is31fl3741_set_current(busio_i2c_obj_t *i2c, uint8_t addr, uint8_t current) { + is31fl3741_set_page(i2c, addr, 4); + uint8_t gcur[2] = { 0x01, 0x00 }; // global current command + gcur[1] = current; + common_hal_busio_i2c_write(i2c, addr, gcur, 2, true); +} + +uint8_t is31fl3741_get_current(busio_i2c_obj_t *i2c, uint8_t addr) { + is31fl3741_set_page(i2c, addr, 4); + uint8_t gcur = 0x01; // global current command + common_hal_busio_i2c_write(i2c, addr, &gcur, 1, true); + + uint8_t data = 0; + common_hal_busio_i2c_read(i2c, addr, &data, 1); + return data; +} + +void is31fl3741_set_led(busio_i2c_obj_t *i2c, uint8_t addr, uint16_t led, uint8_t level, uint8_t page) { + uint8_t cmd[2] = { 0x00, 0x00 }; + + if (led < 180) { + is31fl3741_set_page(i2c, addr, page); + cmd[0] = (uint8_t)led; + } else { + is31fl3741_set_page(i2c, addr, page + 1); + cmd[0] = (uint8_t)(led - 180); + } + + cmd[1] = level; + + common_hal_busio_i2c_write(i2c, addr, cmd, 2, true); +} + +void is31fl3741_draw_pixel(busio_i2c_obj_t *i2c, uint8_t addr, int16_t x, int16_t y, uint32_t color, uint16_t *mapping) { + uint8_t r = color >> 16 & 0xFF; + uint8_t g = color >> 8 & 0xFF; + uint8_t b = color & 0xFF; + + int16_t x1 = (x * 5 + y) * 3; + uint16_t ridx = mapping[x1 + 2]; + if (ridx != 65535) { + uint16_t gidx = mapping[x1 + 1]; + uint16_t bidx = mapping[x1 + 0]; + is31fl3741_set_led(i2c, addr, ridx, r, 0); + is31fl3741_set_led(i2c, addr, gidx, g, 0); + is31fl3741_set_led(i2c, addr, bidx, b, 0); + } +} diff --git a/shared-module/is31fl3741/IS31FL3741.h b/shared-module/is31fl3741/IS31FL3741.h new file mode 100644 index 0000000000..c2ccfde849 --- /dev/null +++ b/shared-module/is31fl3741/IS31FL3741.h @@ -0,0 +1,69 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2021 Mark Komus + * + * 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 "lib/protomatter/src/core.h" +#include "shared-bindings/busio/I2C.h" + +extern const mp_obj_type_t is31fl3741_is31fl3741_type; +typedef struct { + mp_obj_base_t base; + mp_obj_t framebuffer; + mp_buffer_info_t bufinfo; + uint16_t bufsize, width, height, scale_width, scale_height; + busio_i2c_obj_t *i2c; + busio_i2c_obj_t inline_i2c; + uint8_t device_address; + uint16_t *mapping; + uint8_t bit_depth; + bool paused; + bool scale; + bool auto_gamma; +} is31fl3741_IS31FL3741_obj_t; + +// Gamma correction table +static const uint8_t IS31GammaTable[256] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 3, + 3, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 5, 6, + 6, 6, 6, 7, 7, 7, 8, 8, 8, 9, 9, 9, 10, 10, 10, + 11, 11, 11, 12, 12, 13, 13, 13, 14, 14, 15, 15, 16, 16, 17, + 17, 18, 18, 19, 19, 20, 20, 21, 21, 22, 22, 23, 24, 24, 25, + 25, 26, 27, 27, 28, 29, 29, 30, 31, 31, 32, 33, 34, 34, 35, + 36, 37, 38, 38, 39, 40, 41, 42, 42, 43, 44, 45, 46, 47, 48, + 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, + 64, 65, 66, 68, 69, 70, 71, 72, 73, 75, 76, 77, 78, 80, 81, + 82, 84, 85, 86, 88, 89, 90, 92, 93, 94, 96, 97, 99, 100, 102, + 103, 105, 106, 108, 109, 111, 112, 114, 115, 117, 119, 120, 122, 124, 125, + 127, 129, 130, 132, 134, 136, 137, 139, 141, 143, 145, 146, 148, 150, 152, + 154, 156, 158, 160, 162, 164, 166, 168, 170, 172, 174, 176, 178, 180, 182, + 184, 186, 188, 191, 193, 195, 197, 199, 202, 204, 206, 209, 211, 213, 215, + 218, 220, 223, 225, 227, 230, 232, 235, 237, 240, 242, 245, 247, 250, 252, + 255 +}; diff --git a/shared-module/is31fl3741/__init__.c b/shared-module/is31fl3741/__init__.c new file mode 100644 index 0000000000..e69de29bb2 diff --git a/shared-module/is31fl3741/__init__.h b/shared-module/is31fl3741/__init__.h new file mode 100644 index 0000000000..e69de29bb2 diff --git a/shared-module/is31fl3741/allocator.h b/shared-module/is31fl3741/allocator.h new file mode 100644 index 0000000000..43906f54fc --- /dev/null +++ b/shared-module/is31fl3741/allocator.h @@ -0,0 +1,35 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2021 Mark Komus + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#pragma once + +#include +#include "py/gc.h" +#include "py/misc.h" +#include "supervisor/memory.h" + +extern void *common_hal_is31fl3741_allocator_impl(size_t sz); +extern void common_hal_is31fl3741_free_impl(void *); diff --git a/tests/circuitpython-manual/_bmp16.py b/tests/circuitpython-manual/bitmaptools/_bmp16.py similarity index 100% rename from tests/circuitpython-manual/_bmp16.py rename to tests/circuitpython-manual/bitmaptools/_bmp16.py diff --git a/tests/circuitpython-manual/blend.py b/tests/circuitpython-manual/bitmaptools/blend.py similarity index 100% rename from tests/circuitpython-manual/blend.py rename to tests/circuitpython-manual/bitmaptools/blend.py diff --git a/tests/circuitpython-manual/blinka16.bmp b/tests/circuitpython-manual/bitmaptools/blinka16.bmp similarity index 100% rename from tests/circuitpython-manual/blinka16.bmp rename to tests/circuitpython-manual/bitmaptools/blinka16.bmp diff --git a/tests/circuitpython-manual/bitmaptools/dither-atkinson.bmp b/tests/circuitpython-manual/bitmaptools/dither-atkinson.bmp new file mode 100644 index 0000000000..1829b8e7ae Binary files /dev/null and b/tests/circuitpython-manual/bitmaptools/dither-atkinson.bmp differ diff --git a/tests/circuitpython-manual/bitmaptools/dither-floydstenberg.bmp b/tests/circuitpython-manual/bitmaptools/dither-floydstenberg.bmp new file mode 100644 index 0000000000..e7481b0406 Binary files /dev/null and b/tests/circuitpython-manual/bitmaptools/dither-floydstenberg.bmp differ diff --git a/tests/circuitpython-manual/bitmaptools/dither.py b/tests/circuitpython-manual/bitmaptools/dither.py new file mode 100644 index 0000000000..13d0a5be6d --- /dev/null +++ b/tests/circuitpython-manual/bitmaptools/dither.py @@ -0,0 +1,18 @@ +import bitmaptools +import displayio +import _bmp16 + +if "/" in __file__: + here = __file__.rsplit("/", 1)[0] +else: + here = "." + +c = displayio.Colorspace.BGR565 + +b1 = _bmp16.loadbmp16(here + "/minerva16.bmp") +b3 = displayio.Bitmap(320, 240, 65536) + +bitmaptools.dither(b3, b1, c) +_bmp16.writebmp16(f"dither-atkinson.bmp", b3) +bitmaptools.dither(b3, b1, c, bitmaptools.DitherAlgorithm.FloydStenberg) +_bmp16.writebmp16(f"dither-floydstenberg.bmp", b3) diff --git a/tests/circuitpython-manual/bitmaptools/ellipse.bmp b/tests/circuitpython-manual/bitmaptools/ellipse.bmp new file mode 100644 index 0000000000..c048cb17c7 Binary files /dev/null and b/tests/circuitpython-manual/bitmaptools/ellipse.bmp differ diff --git a/tests/circuitpython-manual/minerva16.bmp b/tests/circuitpython-manual/bitmaptools/minerva16.bmp similarity index 100% rename from tests/circuitpython-manual/minerva16.bmp rename to tests/circuitpython-manual/bitmaptools/minerva16.bmp diff --git a/tests/circuitpython-manual/dither.py b/tests/circuitpython-manual/dither.py deleted file mode 100644 index 49a63c458c..0000000000 --- a/tests/circuitpython-manual/dither.py +++ /dev/null @@ -1,31 +0,0 @@ -import bitmaptools -import displayio -import _bmp16 - -if "/" in __file__: - here = __file__.rsplit("/", 1)[0] -else: - here = "." - -c = displayio.Colorspace.BGR565 - -b1 = _bmp16.loadbmp16(here + "/minerva16.bmp") -b3 = displayio.Bitmap(320, 240, 65536) - -for i in ( - 0, - 1 / 64, - 3 / 64, - 3 / 32, - 3 / 16, - 0.5, - 1 - 3 / 16, - 1 - 3 / 32, - 1 - 3 / 64, - 1 - 1 / 64, - 1, -): - bitmaptools.dither(b3, b1, c) - _bmp16.writebmp16(f"dither-atkinson.bmp", b3) - bitmaptools.dither(b3, b1, c, bitmaptools.DitherAlgorithm.FloydStenberg) - _bmp16.writebmp16(f"dither-floydstenberg.bmp", b3)