diff --git a/ports/nrf/boards/microbit/modules/AUTHORS b/ports/nrf/boards/microbit/modules/AUTHORS new file mode 100644 index 0000000000..60ed2e52ed --- /dev/null +++ b/ports/nrf/boards/microbit/modules/AUTHORS @@ -0,0 +1,9 @@ +Damien P. George (@dpgeorge) +Nicholas H. Tollervey (@ntoll) +Matthew Else (@matthewelse) +Alan M. Jackson (@alanmjackson) +Mark Shannon (@markshannon) +Larry Hastings (@larryhastings) +Mariia Koroliuk (@marichkakorolyuk) +Andrew Mulholland (@gbaman) +Joe Glancy (@JoeGlancy) diff --git a/ports/nrf/boards/microbit/modules/LICENSE b/ports/nrf/boards/microbit/modules/LICENSE new file mode 100644 index 0000000000..621229028a --- /dev/null +++ b/ports/nrf/boards/microbit/modules/LICENSE @@ -0,0 +1,22 @@ +The MIT License (MIT) + +Copyright (c) 2013-2016 The MicroPython-on-micro:bit Developers, as listed +in the accompanying AUTHORS file + +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. diff --git a/ports/nrf/boards/microbit/modules/iters.c b/ports/nrf/boards/microbit/modules/iters.c new file mode 100644 index 0000000000..a024793edb --- /dev/null +++ b/ports/nrf/boards/microbit/modules/iters.c @@ -0,0 +1,70 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2015/6 Mark Shannon + * + * 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/runtime.h" +#include "lib/iters.h" + + +typedef struct _repeat_iterator_t { + mp_obj_base_t base; + mp_obj_t iterable; + mp_int_t index; +} repeat_iterator_t; + +static mp_obj_t microbit_repeat_iter_next(mp_obj_t iter_in) { + repeat_iterator_t *iter = (repeat_iterator_t *)iter_in; + iter->index++; + if (iter->index >= mp_obj_get_int(mp_obj_len(iter->iterable))) { + iter->index = 0; + } + return mp_obj_subscr(iter->iterable, MP_OBJ_NEW_SMALL_INT(iter->index), MP_OBJ_SENTINEL); +} + +const mp_obj_type_t microbit_repeat_iterator_type = { + { &mp_type_type }, + .name = MP_QSTR_iterator, + .print = NULL, + .make_new = NULL, + .call = NULL, + .unary_op = NULL, + .binary_op = NULL, + .attr = NULL, + .subscr = NULL, + .getiter = mp_identity, + .iternext = microbit_repeat_iter_next, + .buffer_p = {NULL}, + .stream_p = NULL, + .bases_tuple = MP_OBJ_NULL, + MP_OBJ_NULL +}; + +mp_obj_t microbit_repeat_iterator(mp_obj_t iterable) { + repeat_iterator_t *result = m_new_obj(repeat_iterator_t); + result->base.type = µbit_repeat_iterator_type; + result->iterable = iterable; + result->index = -1; + return result; +} diff --git a/ports/nrf/boards/microbit/modules/iters.h b/ports/nrf/boards/microbit/modules/iters.h new file mode 100644 index 0000000000..f7f716c8bf --- /dev/null +++ b/ports/nrf/boards/microbit/modules/iters.h @@ -0,0 +1,4 @@ + +#include "py/runtime.h" + +mp_obj_t microbit_repeat_iterator(mp_obj_t iterable); diff --git a/ports/nrf/boards/microbit/modules/microbitconstimage.cpp b/ports/nrf/boards/microbit/modules/microbitconstimage.cpp new file mode 100644 index 0000000000..c6c38d0c3a --- /dev/null +++ b/ports/nrf/boards/microbit/modules/microbitconstimage.cpp @@ -0,0 +1,562 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2015 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 + * 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 "microbitobj.h" + +extern "C" { + +#include "py/runtime.h" +#include "modmicrobit.h" +#include "microbitimage.h" + + +#define IMAGE_T const monochrome_5by5_t + +IMAGE_T microbit_const_image_heart_obj = SMALL_IMAGE( + 0,1,0,1,0, + 1,1,1,1,1, + 1,1,1,1,1, + 0,1,1,1,0, + 0,0,1,0,0 +); + +IMAGE_T microbit_const_image_heart_small_obj = SMALL_IMAGE( + 0,0,0,0,0, + 0,1,0,1,0, + 0,1,1,1,0, + 0,0,1,0,0, + 0,0,0,0,0 +); + +// smilies + +IMAGE_T microbit_const_image_happy_obj = SMALL_IMAGE( + 0,0,0,0,0, + 0,1,0,1,0, + 0,0,0,0,0, + 1,0,0,0,1, + 0,1,1,1,0 +); + +IMAGE_T microbit_const_image_smile_obj = SMALL_IMAGE( + 0,0,0,0,0, + 0,0,0,0,0, + 0,0,0,0,0, + 1,0,0,0,1, + 0,1,1,1,0 +); + +IMAGE_T microbit_const_image_sad_obj = SMALL_IMAGE( + 0,0,0,0,0, + 0,1,0,1,0, + 0,0,0,0,0, + 0,1,1,1,0, + 1,0,0,0,1 +); + +IMAGE_T microbit_const_image_confused_obj = SMALL_IMAGE( + 0,0,0,0,0, + 0,1,0,1,0, + 0,0,0,0,0, + 0,1,0,1,0, + 1,0,1,0,1 +); + +IMAGE_T microbit_const_image_angry_obj = SMALL_IMAGE( + 1,0,0,0,1, + 0,1,0,1,0, + 0,0,0,0,0, + 1,1,1,1,1, + 1,0,1,0,1 +); + +IMAGE_T microbit_const_image_asleep_obj = SMALL_IMAGE( + 0,0,0,0,0, + 1,1,0,1,1, + 0,0,0,0,0, + 0,1,1,1,0, + 0,0,0,0,0 +); + +IMAGE_T microbit_const_image_surprised_obj = SMALL_IMAGE( + 0,1,0,1,0, + 0,0,0,0,0, + 0,0,1,0,0, + 0,1,0,1,0, + 0,0,1,0,0 +); + +IMAGE_T microbit_const_image_silly_obj = SMALL_IMAGE( + 1,0,0,0,1, + 0,0,0,0,0, + 1,1,1,1,1, + 0,0,1,0,1, + 0,0,1,1,1 +); + +IMAGE_T microbit_const_image_fabulous_obj = SMALL_IMAGE( + 1,1,1,1,1, + 1,1,0,1,1, + 0,0,0,0,0, + 0,1,0,1,0, + 0,1,1,1,0 +); + +IMAGE_T microbit_const_image_meh_obj = SMALL_IMAGE( + 0,1,0,1,0, + 0,0,0,0,0, + 0,0,0,1,0, + 0,0,1,0,0, + 0,1,0,0,0 +); + +// yes/no + +IMAGE_T microbit_const_image_yes_obj = SMALL_IMAGE( + 0,0,0,0,0, + 0,0,0,0,1, + 0,0,0,1,0, + 1,0,1,0,0, + 0,1,0,0,0 +); + +IMAGE_T microbit_const_image_no_obj = SMALL_IMAGE( + 1,0,0,0,1, + 0,1,0,1,0, + 0,0,1,0,0, + 0,1,0,1,0, + 1,0,0,0,1 +); + +// clock hands + +IMAGE_T microbit_const_image_clock12_obj = SMALL_IMAGE( + 0,0,1,0,0, + 0,0,1,0,0, + 0,0,1,0,0, + 0,0,0,0,0, + 0,0,0,0,0 +); + +IMAGE_T microbit_const_image_clock1_obj = SMALL_IMAGE( + 0,0,0,1,0, + 0,0,0,1,0, + 0,0,1,0,0, + 0,0,0,0,0, + 0,0,0,0,0 +); + +IMAGE_T microbit_const_image_clock2_obj = SMALL_IMAGE( + 0,0,0,0,0, + 0,0,0,1,1, + 0,0,1,0,0, + 0,0,0,0,0, + 0,0,0,0,0 +); + +IMAGE_T microbit_const_image_clock3_obj = SMALL_IMAGE( + 0,0,0,0,0, + 0,0,0,0,0, + 0,0,1,1,1, + 0,0,0,0,0, + 0,0,0,0,0 +); + +IMAGE_T microbit_const_image_clock4_obj = SMALL_IMAGE( + 0,0,0,0,0, + 0,0,0,0,0, + 0,0,1,0,0, + 0,0,0,1,1, + 0,0,0,0,0 +); + +IMAGE_T microbit_const_image_clock5_obj = SMALL_IMAGE( + 0,0,0,0,0, + 0,0,0,0,0, + 0,0,1,0,0, + 0,0,0,1,0, + 0,0,0,1,0 +); + +IMAGE_T microbit_const_image_clock6_obj = SMALL_IMAGE( + 0,0,0,0,0, + 0,0,0,0,0, + 0,0,1,0,0, + 0,0,1,0,0, + 0,0,1,0,0 +); + +IMAGE_T microbit_const_image_clock7_obj = SMALL_IMAGE( + 0,0,0,0,0, + 0,0,0,0,0, + 0,0,1,0,0, + 0,1,0,0,0, + 0,1,0,0,0 +); + +IMAGE_T microbit_const_image_clock8_obj = SMALL_IMAGE( + 0,0,0,0,0, + 0,0,0,0,0, + 0,0,1,0,0, + 1,1,0,0,0, + 0,0,0,0,0 +); + +IMAGE_T microbit_const_image_clock9_obj = SMALL_IMAGE( + 0,0,0,0,0, + 0,0,0,0,0, + 1,1,1,0,0, + 0,0,0,0,0, + 0,0,0,0,0 +); + +IMAGE_T microbit_const_image_clock10_obj = SMALL_IMAGE( + 0,0,0,0,0, + 1,1,0,0,0, + 0,0,1,0,0, + 0,0,0,0,0, + 0,0,0,0,0 +); + +IMAGE_T microbit_const_image_clock11_obj = SMALL_IMAGE( + 0,1,0,0,0, + 0,1,0,0,0, + 0,0,1,0,0, + 0,0,0,0,0, + 0,0,0,0,0 +); + +// arrows + +IMAGE_T microbit_const_image_arrow_n_obj = SMALL_IMAGE( + 0,0,1,0,0, + 0,1,1,1,0, + 1,0,1,0,1, + 0,0,1,0,0, + 0,0,1,0,0 +); + +IMAGE_T microbit_const_image_arrow_ne_obj = SMALL_IMAGE( + 0,0,1,1,1, + 0,0,0,1,1, + 0,0,1,0,1, + 0,1,0,0,0, + 1,0,0,0,0 +); + +IMAGE_T microbit_const_image_arrow_e_obj = SMALL_IMAGE( + 0,0,1,0,0, + 0,0,0,1,0, + 1,1,1,1,1, + 0,0,0,1,0, + 0,0,1,0,0 +); + +IMAGE_T microbit_const_image_arrow_se_obj = SMALL_IMAGE( + 1,0,0,0,0, + 0,1,0,0,0, + 0,0,1,0,1, + 0,0,0,1,1, + 0,0,1,1,1 +); + +IMAGE_T microbit_const_image_arrow_s_obj = SMALL_IMAGE( + 0,0,1,0,0, + 0,0,1,0,0, + 1,0,1,0,1, + 0,1,1,1,0, + 0,0,1,0,0 +); + +IMAGE_T microbit_const_image_arrow_sw_obj = SMALL_IMAGE( + 0,0,0,0,1, + 0,0,0,1,0, + 1,0,1,0,0, + 1,1,0,0,0, + 1,1,1,0,0 +); + +IMAGE_T microbit_const_image_arrow_w_obj = SMALL_IMAGE( + 0,0,1,0,0, + 0,1,0,0,0, + 1,1,1,1,1, + 0,1,0,0,0, + 0,0,1,0,0 +); + +IMAGE_T microbit_const_image_arrow_nw_obj = SMALL_IMAGE( + 1,1,1,0,0, + 1,1,0,0,0, + 1,0,1,0,0, + 0,0,0,1,0, + 0,0,0,0,1 +); + +// geometry + +IMAGE_T microbit_const_image_triangle_obj = SMALL_IMAGE( + 0,0,0,0,0, + 0,0,1,0,0, + 0,1,0,1,0, + 1,1,1,1,1, + 0,0,0,0,0 +); + +IMAGE_T microbit_const_image_triangle_left_obj = SMALL_IMAGE( + 1,0,0,0,0, + 1,1,0,0,0, + 1,0,1,0,0, + 1,0,0,1,0, + 1,1,1,1,1 +); + +IMAGE_T microbit_const_image_chessboard_obj = SMALL_IMAGE( + 0,1,0,1,0, + 1,0,1,0,1, + 0,1,0,1,0, + 1,0,1,0,1, + 0,1,0,1,0 +); + +IMAGE_T microbit_const_image_diamond_obj = SMALL_IMAGE( + 0,0,1,0,0, + 0,1,0,1,0, + 1,0,0,0,1, + 0,1,0,1,0, + 0,0,1,0,0 +); + +IMAGE_T microbit_const_image_diamond_small_obj = SMALL_IMAGE( + 0,0,0,0,0, + 0,0,1,0,0, + 0,1,0,1,0, + 0,0,1,0,0, + 0,0,0,0,0 +); + +IMAGE_T microbit_const_image_square_obj = SMALL_IMAGE( + 1,1,1,1,1, + 1,0,0,0,1, + 1,0,0,0,1, + 1,0,0,0,1, + 1,1,1,1,1 +); + +IMAGE_T microbit_const_image_square_small_obj = SMALL_IMAGE( + 0,0,0,0,0, + 0,1,1,1,0, + 0,1,0,1,0, + 0,1,1,1,0, + 0,0,0,0,0 +); + +// animals + +IMAGE_T microbit_const_image_rabbit = SMALL_IMAGE( + 1,0,1,0,0, + 1,0,1,0,0, + 1,1,1,1,0, + 1,1,0,1,0, + 1,1,1,1,0 +); + +IMAGE_T microbit_const_image_cow = SMALL_IMAGE( + 1,0,0,0,1, + 1,0,0,0,1, + 1,1,1,1,1, + 0,1,1,1,0, + 0,0,1,0,0 +); + +// musical notes + +IMAGE_T microbit_const_image_music_crotchet_obj = SMALL_IMAGE( + 0,0,1,0,0, + 0,0,1,0,0, + 0,0,1,0,0, + 1,1,1,0,0, + 1,1,1,0,0 +); + +IMAGE_T microbit_const_image_music_quaver_obj = SMALL_IMAGE( + 0,0,1,0,0, + 0,0,1,1,0, + 0,0,1,0,1, + 1,1,1,0,0, + 1,1,1,0,0 +); + +IMAGE_T microbit_const_image_music_quavers_obj = SMALL_IMAGE( + 0,1,1,1,1, + 0,1,0,0,1, + 0,1,0,0,1, + 1,1,0,1,1, + 1,1,0,1,1 +); + +// other icons + +IMAGE_T microbit_const_image_pitchfork_obj = SMALL_IMAGE( + 1,0,1,0,1, + 1,0,1,0,1, + 1,1,1,1,1, + 0,0,1,0,0, + 0,0,1,0,0 +); + +IMAGE_T microbit_const_image_xmas_obj = SMALL_IMAGE( + 0,0,1,0,0, + 0,1,1,1,0, + 0,0,1,0,0, + 0,1,1,1,0, + 1,1,1,1,1 +); + +IMAGE_T microbit_const_image_pacman_obj = SMALL_IMAGE( + 0,1,1,1,1, + 1,1,0,1,0, + 1,1,1,0,0, + 1,1,1,1,0, + 0,1,1,1,1 +); + +IMAGE_T microbit_const_image_target_obj = SMALL_IMAGE( + 0,0,1,0,0, + 0,1,1,1,0, + 1,1,0,1,1, + 0,1,1,1,0, + 0,0,1,0,0 +); + +/* +The following images were designed by Abbie Brooks. +*/ + +IMAGE_T microbit_const_image_tshirt_obj = SMALL_IMAGE( + 1,1,0,1,1, + 1,1,1,1,1, + 0,1,1,1,0, + 0,1,1,1,0, + 0,1,1,1,0 +); + +IMAGE_T microbit_const_image_rollerskate_obj = SMALL_IMAGE( + 0,0,0,1,1, + 0,0,0,1,1, + 1,1,1,1,1, + 1,1,1,1,1, + 0,1,0,1,0 +); + +IMAGE_T microbit_const_image_duck_obj = SMALL_IMAGE( + 0,1,1,0,0, + 1,1,1,0,0, + 0,1,1,1,1, + 0,1,1,1,0, + 0,0,0,0,0 +); + +IMAGE_T microbit_const_image_house_obj = SMALL_IMAGE( + 0,0,1,0,0, + 0,1,1,1,0, + 1,1,1,1,1, + 0,1,1,1,0, + 0,1,0,1,0 +); + +IMAGE_T microbit_const_image_tortoise_obj = SMALL_IMAGE( + 0,0,0,0,0, + 0,1,1,1,0, + 1,1,1,1,1, + 0,1,0,1,0, + 0,0,0,0,0 +); + +IMAGE_T microbit_const_image_butterfly_obj = SMALL_IMAGE( + 1,1,0,1,1, + 1,1,1,1,1, + 0,0,1,0,0, + 1,1,1,1,1, + 1,1,0,1,1 +); + +IMAGE_T microbit_const_image_stickfigure_obj = SMALL_IMAGE( + 0,0,1,0,0, + 1,1,1,1,1, + 0,0,1,0,0, + 0,1,0,1,0, + 1,0,0,0,1 +); + +IMAGE_T microbit_const_image_ghost_obj = SMALL_IMAGE( + 1,1,1,1,1, + 1,0,1,0,1, + 1,1,1,1,1, + 1,1,1,1,1, + 1,0,1,0,1 +); + +IMAGE_T microbit_const_image_sword_obj = SMALL_IMAGE( + 0,0,1,0,0, + 0,0,1,0,0, + 0,0,1,0,0, + 0,1,1,1,0, + 0,0,1,0,0 +); + +IMAGE_T microbit_const_image_giraffe_obj = SMALL_IMAGE( + 1,1,0,0,0, + 0,1,0,0,0, + 0,1,0,0,0, + 0,1,1,1,0, + 0,1,0,1,0 +); + +IMAGE_T microbit_const_image_skull_obj = SMALL_IMAGE( + 0,1,1,1,0, + 1,0,1,0,1, + 1,1,1,1,1, + 0,1,1,1,0, + 0,1,1,1,0 +); + +IMAGE_T microbit_const_image_umbrella_obj = SMALL_IMAGE( + 0,1,1,1,0, + 1,1,1,1,1, + 0,0,1,0,0, + 1,0,1,0,0, + 0,1,1,0,0 +); + +IMAGE_T microbit_const_image_snake_obj = SMALL_IMAGE( + 1,1,0,0,0, + 1,1,0,1,1, + 0,1,0,1,0, + 0,1,1,1,0, + 0,0,0,0,0 +); + +} diff --git a/ports/nrf/boards/microbit/modules/microbitconstimagetuples.c b/ports/nrf/boards/microbit/modules/microbitconstimagetuples.c new file mode 100644 index 0000000000..0779d16510 --- /dev/null +++ b/ports/nrf/boards/microbit/modules/microbitconstimagetuples.c @@ -0,0 +1,60 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2015 Mark Shannon + * + * 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/runtime.h" +#include "modmicrobit.h" + +const mp_obj_tuple_t microbit_const_image_all_clocks_tuple_obj = { + {&mp_type_tuple}, + .len = 12, + .items = { + (mp_obj_t)µbit_const_image_clock12_obj, + (mp_obj_t)µbit_const_image_clock1_obj, + (mp_obj_t)µbit_const_image_clock2_obj, + (mp_obj_t)µbit_const_image_clock3_obj, + (mp_obj_t)µbit_const_image_clock4_obj, + (mp_obj_t)µbit_const_image_clock5_obj, + (mp_obj_t)µbit_const_image_clock6_obj, + (mp_obj_t)µbit_const_image_clock7_obj, + (mp_obj_t)µbit_const_image_clock8_obj, + (mp_obj_t)µbit_const_image_clock9_obj, + (mp_obj_t)µbit_const_image_clock10_obj, + (mp_obj_t)µbit_const_image_clock11_obj + } +}; + +const mp_obj_tuple_t microbit_const_image_all_arrows_tuple_obj = { + {&mp_type_tuple}, + .len = 8, + .items = { + (mp_obj_t)µbit_const_image_arrow_n_obj, + (mp_obj_t)µbit_const_image_arrow_ne_obj, + (mp_obj_t)µbit_const_image_arrow_e_obj, + (mp_obj_t)µbit_const_image_arrow_se_obj, + (mp_obj_t)µbit_const_image_arrow_s_obj, + (mp_obj_t)µbit_const_image_arrow_sw_obj, + (mp_obj_t)µbit_const_image_arrow_w_obj, + (mp_obj_t)µbit_const_image_arrow_nw_obj + } +}; diff --git a/ports/nrf/boards/microbit/modules/microbitdisplay.cpp b/ports/nrf/boards/microbit/modules/microbitdisplay.cpp new file mode 100644 index 0000000000..92bf58d826 --- /dev/null +++ b/ports/nrf/boards/microbit/modules/microbitdisplay.cpp @@ -0,0 +1,579 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2015 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 + * 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 "microbitobj.h" +#include "nrf_gpio.h" + +extern "C" { +#include "py/runtime.h" +#include "py/gc.h" +#include "modmicrobit.h" +#include "microbitimage.h" +#include "microbitdisplay.h" +#include "microbitpin.h" +#include "lib/iters.h" +#include "lib/ticker.h" + +#define min(a,b) (((a)<(b))?(a):(b)) + +void microbit_display_show(microbit_display_obj_t *display, microbit_image_obj_t *image) { + mp_int_t w = min(image->width(), 5); + mp_int_t h = min(image->height(), 5); + mp_int_t x = 0; + mp_int_t brightnesses = 0; + for (; x < w; ++x) { + mp_int_t y = 0; + for (; y < h; ++y) { + uint8_t pix = image->getPixelValue(x, y); + display->image_buffer[x][y] = pix; + brightnesses |= (1 << pix); + } + for (; y < 5; ++y) { + display->image_buffer[x][y] = 0; + } + } + for (; x < 5; ++x) { + for (mp_int_t y = 0; y < 5; ++y) { + display->image_buffer[x][y] = 0; + } + } + display->brightnesses = brightnesses; +} + +#define DEFAULT_PRINT_SPEED 400 + + +mp_obj_t microbit_display_show_func(mp_uint_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + + // Cancel any animations. + MP_STATE_PORT(async_data)[0] = NULL; + MP_STATE_PORT(async_data)[1] = NULL; + + static const mp_arg_t show_allowed_args[] = { + { MP_QSTR_image, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} }, + { MP_QSTR_delay, MP_ARG_INT, {.u_int = DEFAULT_PRINT_SPEED} }, + { MP_QSTR_clear, MP_ARG_KW_ONLY | MP_ARG_BOOL, {.u_bool = false} }, + { MP_QSTR_wait, MP_ARG_KW_ONLY | MP_ARG_BOOL, {.u_bool = true} }, + { MP_QSTR_loop, MP_ARG_KW_ONLY | MP_ARG_BOOL, {.u_bool = false} }, + }; + + // Parse the args. + microbit_display_obj_t *self = (microbit_display_obj_t*)pos_args[0]; + mp_arg_val_t args[MP_ARRAY_SIZE(show_allowed_args)]; + mp_arg_parse_all(n_args - 1, pos_args + 1, kw_args, MP_ARRAY_SIZE(show_allowed_args), show_allowed_args, args); + + mp_obj_t image = args[0].u_obj; + mp_int_t delay = args[1].u_int; + bool clear = args[2].u_bool; + bool wait = args[3].u_bool; + bool loop = args[4].u_bool; + + if (MP_OBJ_IS_STR(image)) { + // arg is a string object + mp_uint_t len; + const char *str = mp_obj_str_get_data(image, &len); + if (len == 0) { + // There are no chars; do nothing. + return mp_const_none; + } else if (len == 1) { + if (!clear && !loop) { + // A single char; convert to an image and print that. + image = microbit_image_for_char(str[0]); + goto single_image_immediate; + } + } + image = microbit_string_facade(image); + } else if (mp_obj_get_type(image) == µbit_image_type) { + if (!clear && !loop) { + goto single_image_immediate; + } + image = mp_obj_new_tuple(1, &image); + } + // iterable: + if (args[4].u_bool) { /*loop*/ + image = microbit_repeat_iterator(image); + } + microbit_display_animate(self, image, delay, clear, wait); + return mp_const_none; + +single_image_immediate: + microbit_display_show(self, (microbit_image_obj_t *)image); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_KW(microbit_display_show_obj, 1, microbit_display_show_func); + +static uint8_t async_mode; +static mp_obj_t async_iterator = NULL; +// Record if an error occurs in async animation. Unfortunately there is no way to report this. +static volatile bool wakeup_event = false; +static mp_uint_t async_delay = 1000; +static mp_uint_t async_tick = 0; +static bool async_clear = false; + + +bool microbit_display_active_animation(void) { + return async_mode == ASYNC_MODE_ANIMATION; +} + +STATIC void async_stop(void) { + async_iterator = NULL; + async_mode = ASYNC_MODE_STOPPED; + async_tick = 0; + async_delay = 1000; + async_clear = false; + MP_STATE_PORT(async_data)[0] = NULL; + MP_STATE_PORT(async_data)[1] = NULL; + wakeup_event = true; +} + +STATIC void wait_for_event() { + while (!wakeup_event) { + // allow CTRL-C to stop the animation + if (MP_STATE_VM(mp_pending_exception) != MP_OBJ_NULL) { + async_stop(); + return; + } + __WFI(); + } + wakeup_event = false; +} + +struct DisplayPoint { + uint8_t x; + uint8_t y; +}; + +#define NO_CONN 0 + +#define ROW_COUNT 3 +#define COLUMN_COUNT 9 + +static const DisplayPoint display_map[COLUMN_COUNT][ROW_COUNT] = { + {{0,0}, {4,2}, {2,4}}, + {{2,0}, {0,2}, {4,4}}, + {{4,0}, {2,2}, {0,4}}, + {{4,3}, {1,0}, {0,1}}, + {{3,3}, {3,0}, {1,1}}, + {{2,3}, {3,4}, {2,1}}, + {{1,3}, {1,4}, {3,1}}, + {{0,3}, {NO_CONN,NO_CONN}, {4,1}}, + {{1,2}, {NO_CONN,NO_CONN}, {3,2}} +}; + +#define MIN_COLUMN_PIN 4 +#define COLUMN_PINS_MASK 0x1ff0 +#define MIN_ROW_PIN 13 +#define MAX_ROW_PIN 15 +#define ROW_PINS_MASK 0xe000 + +inline void microbit_display_obj_t::setPinsForRow(uint8_t brightness) { + if (brightness == 0) { + nrf_gpio_pins_clear(COLUMN_PINS_MASK & ~this->pins_for_brightness[brightness]); + } else { + nrf_gpio_pins_set(this->pins_for_brightness[brightness]); + } +} + +/* This is the primary PWM driver/display driver. It will operate on one row + * (9 pins) per invocation. It will turn on LEDs with maximum brightness, + * then let the "callback" callback turn off the LEDs as appropriate for the + * required brightness level. + * + * For each row + * Turn off all the LEDs in the previous row + * Set the column bits high (off) + * Set the row strobe low (off) + * Turn on all the LEDs in the current row that have maximum brightness + * Set the row strobe high (on) + * Set some/all column bits low (on) + * Register the PWM callback + * For each callback start with brightness 0 + * If brightness 0 + * Turn off the LEDs specified at this level + * Else + * Turn on the LEDs specified at this level + * If brightness max + * Disable the PWM callback + * Else + * Re-queue the PWM callback after the appropriate delay + */ +void microbit_display_obj_t::advanceRow() { + /* Clear all of the column bits */ + nrf_gpio_pins_set(COLUMN_PINS_MASK); + /* Clear the strobe bit for this row */ + nrf_gpio_pin_clear(strobe_row+MIN_ROW_PIN); + + /* Move to the next row. Before this, "this row" refers to the row + * manipulated by the previous invocation of this function. After this, + * "this row" refers to the row manipulated by the current invocation of + * this function. */ + strobe_row++; + + // Reset the row counts and bit mask when we have hit the max. + if (strobe_row == ROW_COUNT) { + strobe_row = 0; + } + + // Set pin for this row. + // Prepare row for rendering. + for (int i = 0; i <= MAX_BRIGHTNESS; i++) { + pins_for_brightness[i] = 0; + } + for (int i = 0; i < COLUMN_COUNT; i++) { + int x = display_map[i][strobe_row].x; + int y = display_map[i][strobe_row].y; + uint8_t brightness = microbit_display_obj.image_buffer[x][y]; + pins_for_brightness[brightness] |= (1<<(i+MIN_COLUMN_PIN)); + } + /* Enable the strobe bit for this row */ + nrf_gpio_pin_set(strobe_row+MIN_ROW_PIN); + /* Enable the column bits for all pins that need to be on. */ + nrf_gpio_pins_clear(pins_for_brightness[MAX_BRIGHTNESS]); +} + +static const uint16_t render_timings[] = +// The scale is (approximately) exponential, +// each step is approx x1.9 greater than the previous. +{ 0, // Bright, Ticks Duration, Relative power + 2, // 1, 2, 32µs, inf + 2, // 2, 4, 64µs, 200% + 4, // 3, 8, 128µs, 200% + 7, // 4, 15, 240µs, 187% + 13, // 5, 28, 448µs, 187% + 25, // 6, 53, 848µs, 189% + 49, // 7, 102, 1632µs, 192% + 97, // 8, 199, 3184µs, 195% +// Always on 9, 375, 6000µs, 188% +}; + +#define DISPLAY_TICKER_SLOT 1 + +/* This is the PWM callback. It is registered by the animation callback and + * will unregister itself when all of the brightness steps are complete. */ +static int32_t callback(void) { + microbit_display_obj_t *display = µbit_display_obj; + mp_uint_t brightness = display->previous_brightness; + display->setPinsForRow(brightness); + brightness += 1; + if (brightness == MAX_BRIGHTNESS) { + clear_ticker_callback(DISPLAY_TICKER_SLOT); + return -1; + } + display->previous_brightness = brightness; + // Return interval (in 16µs ticks) until next callback + return render_timings[brightness]; +} + +static void draw_object(mp_obj_t obj) { + microbit_display_obj_t *display = (microbit_display_obj_t*)MP_STATE_PORT(async_data)[0]; + if (obj == MP_OBJ_STOP_ITERATION) { + if (async_clear) { + microbit_display_show(µbit_display_obj, BLANK_IMAGE); + async_clear = false; + } else { + async_stop(); + } + } else if (mp_obj_get_type(obj) == µbit_image_type) { + microbit_display_show(display, (microbit_image_obj_t *)obj); + } else if (MP_OBJ_IS_STR(obj)) { + mp_uint_t len; + const char *str = mp_obj_str_get_data(obj, &len); + if (len == 1) { + microbit_display_show(display, microbit_image_for_char(str[0])); + } else { + async_stop(); + } + } else { + MP_STATE_VM(mp_pending_exception) = mp_obj_new_exception_msg(&mp_type_TypeError, "not an image."); + async_stop(); + } +} + +static void microbit_display_update(void) { + async_tick += MILLISECONDS_PER_MACRO_TICK; + if (async_tick < async_delay) { + return; + } + async_tick = 0; + switch (async_mode) { + case ASYNC_MODE_ANIMATION: + { + if (MP_STATE_PORT(async_data)[0] == NULL || MP_STATE_PORT(async_data)[1] == NULL) { + async_stop(); + break; + } + /* WARNING: We are executing in an interrupt handler. + * If an exception is raised here then we must hand it to the VM. */ + mp_obj_t obj; + nlr_buf_t nlr; + gc_lock(); + if (nlr_push(&nlr) == 0) { + obj = mp_iternext_allow_raise(async_iterator); + nlr_pop(); + gc_unlock(); + } else { + gc_unlock(); + if (!mp_obj_is_subclass_fast(MP_OBJ_FROM_PTR(((mp_obj_base_t*)nlr.ret_val)->type), + MP_OBJ_FROM_PTR(&mp_type_StopIteration))) { + // An exception other than StopIteration, so set it for the VM to raise later + // If memory error, write an appropriate message. + if (mp_obj_get_type(nlr.ret_val) == &mp_type_MemoryError) { + mp_printf(&mp_plat_print, "Allocation in interrupt handler"); + } + MP_STATE_VM(mp_pending_exception) = MP_OBJ_FROM_PTR(nlr.ret_val); + } + obj = MP_OBJ_STOP_ITERATION; + } + draw_object(obj); + break; + } + case ASYNC_MODE_CLEAR: + microbit_display_show(µbit_display_obj, BLANK_IMAGE); + async_stop(); + break; + } +} + +#define GREYSCALE_MASK ((1<active = true; + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_1(microbit_display_on_obj, microbit_display_on_func); + +mp_obj_t microbit_display_off_func(mp_obj_t obj) { + microbit_display_obj_t *self = (microbit_display_obj_t*)obj; + /* Disable the display loop. This will pause any animations in progress. + * It will not prevent a user from attempting to modify the state, but + * modifications will not appear to have any effect until the display loop + * is re-enabled. */ + self->active = false; + /* Disable the row strobes, allowing the columns to be used freely for + * GPIO. */ + nrf_gpio_pins_clear(ROW_PINS_MASK); + /* Free pins for other uses */ + microbit_obj_pin_free(µbit_p3_obj); + microbit_obj_pin_free(µbit_p4_obj); + microbit_obj_pin_free(µbit_p6_obj); + microbit_obj_pin_free(µbit_p7_obj); + microbit_obj_pin_free(µbit_p9_obj); + microbit_obj_pin_free(µbit_p10_obj); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_1(microbit_display_off_obj, microbit_display_off_func); + +mp_obj_t microbit_display_is_on_func(mp_obj_t obj) { + microbit_display_obj_t *self = (microbit_display_obj_t*)obj; + if (self->active) { + return mp_const_true; + } + else { + return mp_const_false; + } +} +MP_DEFINE_CONST_FUN_OBJ_1(microbit_display_is_on_obj, microbit_display_is_on_func); + +void microbit_display_clear(void) { + // Reset repeat state, cancel animation and clear screen. + wakeup_event = false; + async_mode = ASYNC_MODE_CLEAR; + async_tick = async_delay - MILLISECONDS_PER_MACRO_TICK; + wait_for_event(); +} + +mp_obj_t microbit_display_clear_func(void) { + microbit_display_clear(); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_1(microbit_display_clear_obj, microbit_display_clear_func); + +void microbit_display_set_pixel(microbit_display_obj_t *display, mp_int_t x, mp_int_t y, mp_int_t bright) { + if (x < 0 || y < 0 || x > 4 || y > 4) { + nlr_raise(mp_obj_new_exception_msg(&mp_type_ValueError, "index out of bounds.")); + } + if (bright < 0 || bright > MAX_BRIGHTNESS) { + nlr_raise(mp_obj_new_exception_msg(&mp_type_ValueError, "brightness out of bounds.")); + } + display->image_buffer[x][y] = bright; + display->brightnesses |= (1 << bright); +} + +STATIC mp_obj_t microbit_display_set_pixel_func(mp_uint_t n_args, const mp_obj_t *args) { + (void)n_args; + microbit_display_obj_t *self = (microbit_display_obj_t*)args[0]; + microbit_display_set_pixel(self, mp_obj_get_int(args[1]), mp_obj_get_int(args[2]), mp_obj_get_int(args[3])); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(microbit_display_set_pixel_obj, 4, 4, microbit_display_set_pixel_func); + +mp_int_t microbit_display_get_pixel(microbit_display_obj_t *display, mp_int_t x, mp_int_t y) { + if (x < 0 || y < 0 || x > 4 || y > 4) { + nlr_raise(mp_obj_new_exception_msg(&mp_type_ValueError, "index out of bounds.")); + } + return display->image_buffer[x][y]; +} + +STATIC mp_obj_t microbit_display_get_pixel_func(mp_obj_t self_in, mp_obj_t x_in, mp_obj_t y_in) { + microbit_display_obj_t *self = (microbit_display_obj_t*)self_in; + return MP_OBJ_NEW_SMALL_INT(microbit_display_get_pixel(self, mp_obj_get_int(x_in), mp_obj_get_int(y_in))); +} +MP_DEFINE_CONST_FUN_OBJ_3(microbit_display_get_pixel_obj, microbit_display_get_pixel_func); + +STATIC const mp_map_elem_t microbit_display_locals_dict_table[] = { + + { MP_OBJ_NEW_QSTR(MP_QSTR_get_pixel), (mp_obj_t)µbit_display_get_pixel_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_set_pixel), (mp_obj_t)µbit_display_set_pixel_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_show), (mp_obj_t)µbit_display_show_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_scroll), (mp_obj_t)µbit_display_scroll_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_clear), (mp_obj_t)µbit_display_clear_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_on), (mp_obj_t)µbit_display_on_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_off), (mp_obj_t)µbit_display_off_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_is_on), (mp_obj_t)µbit_display_is_on_obj }, +}; + +STATIC MP_DEFINE_CONST_DICT(microbit_display_locals_dict, microbit_display_locals_dict_table); + +STATIC const mp_obj_type_t microbit_display_type = { + { &mp_type_type }, + .name = MP_QSTR_MicroBitDisplay, + .print = NULL, + .make_new = NULL, + .call = NULL, + .unary_op = NULL, + .binary_op = NULL, + .attr = NULL, + .subscr = NULL, + .getiter = NULL, + .iternext = NULL, + .buffer_p = {NULL}, + .stream_p = NULL, + .bases_tuple = NULL, + .locals_dict = (mp_obj_dict_t*)µbit_display_locals_dict, +}; + +microbit_display_obj_t microbit_display_obj = { + {µbit_display_type}, + { 0 }, + .previous_brightness = 0, + .active = 1, + .strobe_row = 0, + .brightnesses = 0, + .pins_for_brightness = { 0 }, +}; + +void microbit_display_init(void) { + // Set pins as output. + nrf_gpio_range_cfg_output(MIN_COLUMN_PIN, MIN_COLUMN_PIN + COLUMN_COUNT + ROW_COUNT); +} + +} diff --git a/ports/nrf/boards/microbit/modules/microbitdisplay.h b/ports/nrf/boards/microbit/modules/microbitdisplay.h new file mode 100644 index 0000000000..24d948af48 --- /dev/null +++ b/ports/nrf/boards/microbit/modules/microbitdisplay.h @@ -0,0 +1,54 @@ + +#ifndef __MICROPY_INCLUDED_MICROBIT_DISPLAY_H__ +#define __MICROPY_INCLUDED_MICROBIT_DISPLAY_H__ + +#include "py/runtime.h" +#include "microbitimage.h" + +typedef struct _microbit_display_obj_t { + mp_obj_base_t base; + uint8_t image_buffer[5][5]; + uint8_t previous_brightness; + bool active; + /* Current row for strobing */ + uint8_t strobe_row; + /* boolean histogram of brightness in buffer */ + uint16_t brightnesses; + uint16_t pins_for_brightness[MAX_BRIGHTNESS+1]; + + void advanceRow(); + inline void setPinsForRow(uint8_t brightness); + + +} microbit_display_obj_t; + +#define ASYNC_MODE_STOPPED 0 +#define ASYNC_MODE_ANIMATION 1 +#define ASYNC_MODE_CLEAR 2 + +extern microbit_display_obj_t microbit_display_obj; + + +extern "C" { + +void microbit_display_show(microbit_display_obj_t *display, microbit_image_obj_t *image); + +void microbit_display_animate(microbit_display_obj_t *display, mp_obj_t iterable, mp_int_t delay, bool clear, bool wait); + +void microbit_display_scroll(microbit_display_obj_t *display, const char* str, bool wait); + +mp_int_t microbit_display_get_pixel(microbit_display_obj_t *display, mp_int_t x, mp_int_t y); + +void microbit_display_set_pixel(microbit_display_obj_t *display, mp_int_t x, mp_int_t y, mp_int_t val); + +void microbit_display_clear(void); + +void microbit_display_init(void); + +void microbit_display_tick(void); + +bool microbit_display_active_animation(void); + +} + +#endif // __MICROPY_INCLUDED_MICROBIT_DISPLAY_H__ diff --git a/ports/nrf/boards/microbit/modules/microbitimage.cpp b/ports/nrf/boards/microbit/modules/microbitimage.cpp new file mode 100644 index 0000000000..60f5526dcb --- /dev/null +++ b/ports/nrf/boards/microbit/modules/microbitimage.cpp @@ -0,0 +1,973 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2015 Damien George, Mark Shannon + * + * 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 "microbitobj.h" +#include "MicroBitFont.h" + +extern "C" { + +#include "py/runtime.h" +#include "modmicrobit.h" +#include "microbitimage.h" +#include "py/runtime0.h" + +#define min(a,b) (((a)<(b))?(a):(b)) +#define max(a,b) (((a)>(b))?(a):(b)) + +const monochrome_5by5_t microbit_blank_image = { + { µbit_image_type }, + 1, 0, 0, 0, + { 0, 0, 0 } +}; + +STATIC void microbit_image_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) { + microbit_image_obj_t *self = (microbit_image_obj_t*)self_in; + mp_printf(print, "Image("); + if (kind == PRINT_STR) + mp_printf(print, "\n "); + mp_printf(print, "'"); + for (int y = 0; y < self->height(); ++y) { + for (int x = 0; x < self->width(); ++x) { + mp_printf(print, "%c", "0123456789"[self->getPixelValue(x, y)]); + } + mp_printf(print, ":"); + if (kind == PRINT_STR && y < self->height()-1) + mp_printf(print, "'\n '"); + } + mp_printf(print, "'"); + if (kind == PRINT_STR) + mp_printf(print, "\n"); + mp_printf(print, ")"); +} + +uint8_t monochrome_5by5_t::getPixelValue(mp_int_t x, mp_int_t y) { + unsigned int index = y*5+x; + if (index == 24) + return this->pixel44; + return (this->bits24[index>>3] >> (index&7))&1; +} + +uint8_t greyscale_t::getPixelValue(mp_int_t x, mp_int_t y) { + unsigned int index = y*this->width+x; + unsigned int shift = ((index<<2)&4); + return (this->byte_data[index>>1] >> shift)&15; +} + +void greyscale_t::setPixelValue(mp_int_t x, mp_int_t y, mp_int_t val) { + unsigned int index = y*this->width+x; + unsigned int shift = ((index<<2)&4); + uint8_t mask = 240 >> shift; + this->byte_data[index>>1] = (this->byte_data[index>>1] & mask) | (val << shift); +} + +void greyscale_t::fill(mp_int_t val) { + mp_int_t byte = (val<<4) | val; + for (int i = 0; i < ((this->width*this->height+1)>>1); i++) { + this->byte_data[i] = byte; + } +} + +void greyscale_t::clear() { + memset(&this->byte_data, 0, (this->width*this->height+1)>>1); +} + +uint8_t microbit_image_obj_t::getPixelValue(mp_int_t x, mp_int_t y) { + if (this->base.five) + return this->monochrome_5by5.getPixelValue(x, y)*MAX_BRIGHTNESS; + else + return this->greyscale.getPixelValue(x, y); +} + +mp_int_t microbit_image_obj_t::width() { + if (this->base.five) + return 5; + else + return this->greyscale.width; +} + +mp_int_t microbit_image_obj_t::height() { + if (this->base.five) + return 5; + else + return this->greyscale.height; +} + +STATIC greyscale_t *greyscale_new(mp_int_t w, mp_int_t h) { + greyscale_t *result = m_new_obj_var(greyscale_t, uint8_t, (w*h+1)>>1); + result->base.type = µbit_image_type; + result->five = 0; + result->width = w; + result->height = h; + return result; +} + +greyscale_t *microbit_image_obj_t::copy() { + mp_int_t w = this->width(); + mp_int_t h = this->height(); + greyscale_t *result = greyscale_new(w, h); + for (mp_int_t y = 0; y < h; y++) { + for (mp_int_t x = 0; x < w; ++x) { + result->setPixelValue(x,y, this->getPixelValue(x,y)); + } + } + return result; +} + +greyscale_t *microbit_image_obj_t::invert() { + mp_int_t w = this->width(); + mp_int_t h = this->height(); + greyscale_t *result = greyscale_new(w, h); + for (mp_int_t y = 0; y < h; y++) { + for (mp_int_t x = 0; x < w; ++x) { + result->setPixelValue(x,y, MAX_BRIGHTNESS - this->getPixelValue(x,y)); + } + } + return result; +} + +STATIC microbit_image_obj_t *image_from_parsed_str(const char *s, mp_int_t len) { + mp_int_t w = 0; + mp_int_t h = 0; + mp_int_t line_len = 0; + greyscale_t *result; + /*First pass -- Establish metadata */ + for (int i = 0; i < len; i++) { + char c = s[i]; + if (c == '\n' || c == ':') { + w = max(line_len, w); + line_len = 0; + ++h; + } else if (c == ' ') { + ++line_len; + } else if ('c' >= '0' && c <= '9') { + ++line_len; + } else { + nlr_raise(mp_obj_new_exception_msg(&mp_type_ValueError, + "Unexpected character in Image definition.")); + } + } + if (line_len) { + // Omitted trailing terminator + ++h; + w = max(line_len, w); + } + result = greyscale_new(w, h); + mp_int_t x = 0; + mp_int_t y = 0; + /* Second pass -- Fill in data */ + for (int i = 0; i < len; i++) { + char c = s[i]; + if (c == '\n' || c == ':') { + while (x < w) { + result->setPixelValue(x, y, 0); + x++; + } + ++y; + x = 0; + } else if (c == ' ') { + /* Treat spaces as 0 */ + result->setPixelValue(x, y, 0); + ++x; + } else if ('c' >= '0' && c <= '9') { + result->setPixelValue(x, y, c - '0'); + ++x; + } + } + if (y < h) { + while (x < w) { + result->setPixelValue(x, y, 0); + x++; + } + } + return (microbit_image_obj_t *)result; +} + + +STATIC mp_obj_t microbit_image_make_new(const mp_obj_type_t *type_in, mp_uint_t n_args, mp_uint_t n_kw, const mp_obj_t *args) { + (void)type_in; + mp_arg_check_num(n_args, n_kw, 0, 3, false); + + switch (n_args) { + case 0: { + greyscale_t *image = greyscale_new(5, 5); + image->clear(); + return image; + } + + case 1: { + if (MP_OBJ_IS_STR(args[0])) { + // arg is a string object + mp_uint_t len; + const char *str = mp_obj_str_get_data(args[0], &len); + // make image from string + if (len == 1) { + /* For a single charater, return the font glyph */ + return microbit_image_for_char(str[0]); + } else { + /* Otherwise parse the image description string */ + return image_from_parsed_str(str, len); + } + } else { + nlr_raise(mp_obj_new_exception_msg(&mp_type_TypeError, + "Image(s) takes a string.")); + } + } + + case 2: + case 3: { + mp_int_t w = mp_obj_get_int(args[0]); + mp_int_t h = mp_obj_get_int(args[1]); + greyscale_t *image = greyscale_new(w, h); + if (n_args == 2) { + image->clear(); + } else { + mp_buffer_info_t bufinfo; + mp_get_buffer_raise(args[2], &bufinfo, MP_BUFFER_READ); + + if (w < 0 || h < 0 || (size_t)(w * h) != bufinfo.len) { + nlr_raise(mp_obj_new_exception_msg(&mp_type_ValueError, + "image data is incorrect size")); + } + mp_int_t i = 0; + for (mp_int_t y = 0; y < h; y++) { + for (mp_int_t x = 0; x < w; ++x) { + uint8_t val = min(((const uint8_t*)bufinfo.buf)[i], MAX_BRIGHTNESS); + image->setPixelValue(x, y, val); + ++i; + } + } + } + return image; + } + + default: { + nlr_raise(mp_obj_new_exception_msg(&mp_type_TypeError, + "Image() takes 0 to 3 arguments")); + } + } +} + +static void clear_rect(greyscale_t *img, mp_int_t x0, mp_int_t y0,mp_int_t x1, mp_int_t y1) { + for (int i = x0; i < x1; ++i) { + for (int j = y0; j < y1; ++j) { + img->setPixelValue(i, j, 0); + } + } +} + +STATIC void image_blit(microbit_image_obj_t *src, greyscale_t *dest, mp_int_t x, mp_int_t y, mp_int_t w, mp_int_t h, mp_int_t xdest, mp_int_t ydest) { + if (w < 0) + w = 0; + if (h < 0) + h = 0; + mp_int_t intersect_x0 = max(max(0, x), -xdest); + mp_int_t intersect_y0 = max(max(0, y), -ydest); + mp_int_t intersect_x1 = min(min(dest->width+x-xdest, src->width()), x+w); + mp_int_t intersect_y1 = min(min(dest->height+y-ydest, src->height()), y+h); + mp_int_t xstart, xend, ystart, yend, xdel, ydel; + mp_int_t clear_x0 = max(0, xdest); + mp_int_t clear_y0 = max(0, ydest); + mp_int_t clear_x1 = min(dest->width, xdest+w); + mp_int_t clear_y1 = min(dest->height, ydest+h); + if (intersect_x0 >= intersect_x1 || intersect_y0 >= intersect_y1) { + // Nothing to copy + clear_rect(dest, clear_x0, clear_y0, clear_x1, clear_y1); + return; + } + if (x > xdest) { + xstart = intersect_x0; xend = intersect_x1; xdel = 1; + } else { + xstart = intersect_x1-1; xend = intersect_x0-1; xdel = -1; + } + if (y > ydest) { + ystart = intersect_y0; yend = intersect_y1; ydel = 1; + } else { + ystart = intersect_y1-1; yend = intersect_y0-1; ydel = -1; + } + for (int i = xstart; i != xend; i += xdel) { + for (int j = ystart; j != yend; j += ydel) { + int val = src->getPixelValue(i, j); + dest->setPixelValue(i+xdest-x, j+ydest-y, val); + } + } + // Adjust intersection rectange to dest + intersect_x0 += xdest-x; + intersect_y0 += ydest-y; + intersect_x1 += xdest-x; + intersect_y1 += ydest-y; + // Clear four rectangles in the cleared area surrounding the copied area. + clear_rect(dest, clear_x0, clear_y0, intersect_x0, intersect_y1); + clear_rect(dest, clear_x0, intersect_y1, intersect_x1, clear_y1); + clear_rect(dest, intersect_x1, intersect_y0, clear_x1, clear_y1); + clear_rect(dest, intersect_x0, clear_y0, clear_x1, intersect_y0); +} + +greyscale_t *image_shift(microbit_image_obj_t *self, mp_int_t x, mp_int_t y) { + greyscale_t *result = greyscale_new(self->width(), self->width()); + image_blit(self, result, x, y, self->width(), self->width(), 0, 0); + return result; +} + +STATIC microbit_image_obj_t *image_crop(microbit_image_obj_t *img, mp_int_t x, mp_int_t y, mp_int_t w, mp_int_t h) { + if (w < 0) + w = 0; + if (h < 0) + h = 0; + greyscale_t *result = greyscale_new(w, h); + image_blit(img, result, x, y, w, h, 0, 0); + return (microbit_image_obj_t *)result; +} + +mp_obj_t microbit_image_width(mp_obj_t self_in) { + microbit_image_obj_t *self = (microbit_image_obj_t*)self_in; + return MP_OBJ_NEW_SMALL_INT(self->width()); +} +MP_DEFINE_CONST_FUN_OBJ_1(microbit_image_width_obj, microbit_image_width); + +mp_obj_t microbit_image_height(mp_obj_t self_in) { + microbit_image_obj_t *self = (microbit_image_obj_t*)self_in; + return MP_OBJ_NEW_SMALL_INT(self->height()); +} +MP_DEFINE_CONST_FUN_OBJ_1(microbit_image_height_obj, microbit_image_height); + +mp_obj_t microbit_image_get_pixel(mp_obj_t self_in, mp_obj_t x_in, mp_obj_t y_in) { + microbit_image_obj_t *self = (microbit_image_obj_t*)self_in; + mp_int_t x = mp_obj_get_int(x_in); + mp_int_t y = mp_obj_get_int(y_in); + if (x < 0 || y < 0) { + nlr_raise(mp_obj_new_exception_msg(&mp_type_ValueError, + "index cannot be negative")); + } + if (x < self->width() && y < self->height()) { + return MP_OBJ_NEW_SMALL_INT(self->getPixelValue(x, y)); + } + nlr_raise(mp_obj_new_exception_msg(&mp_type_ValueError, "index too large")); +} +MP_DEFINE_CONST_FUN_OBJ_3(microbit_image_get_pixel_obj, microbit_image_get_pixel); + +/* Raise an exception if not mutable */ +static void check_mutability(microbit_image_obj_t *self) { + if (self->base.five) { + nlr_raise(mp_obj_new_exception_msg(&mp_type_TypeError, "image cannot be modified (try copying first)")); + } +} + + +mp_obj_t microbit_image_set_pixel(mp_uint_t n_args, const mp_obj_t *args) { + (void)n_args; + microbit_image_obj_t *self = (microbit_image_obj_t*)args[0]; + check_mutability(self); + mp_int_t x = mp_obj_get_int(args[1]); + mp_int_t y = mp_obj_get_int(args[2]); + if (x < 0 || y < 0) { + nlr_raise(mp_obj_new_exception_msg(&mp_type_ValueError, + "index cannot be negative")); + } + mp_int_t bright = mp_obj_get_int(args[3]); + if (bright < 0 || bright > MAX_BRIGHTNESS) + nlr_raise(mp_obj_new_exception_msg(&mp_type_ValueError, "brightness out of bounds.")); + if (x < self->width() && y < self->height()) { + self->greyscale.setPixelValue(x, y, bright); + return mp_const_none; + } + nlr_raise(mp_obj_new_exception_msg(&mp_type_ValueError, "index too large")); +} +MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(microbit_image_set_pixel_obj, 4, 4, microbit_image_set_pixel); + +mp_obj_t microbit_image_fill(mp_obj_t self_in, mp_obj_t n_in) { + microbit_image_obj_t *self = (microbit_image_obj_t*)self_in; + check_mutability(self); + mp_int_t n = mp_obj_get_int(n_in); + if (n < 0 || n > MAX_BRIGHTNESS) { + nlr_raise(mp_obj_new_exception_msg(&mp_type_ValueError, "brightness out of bounds.")); + } + self->greyscale.fill(n); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_2(microbit_image_fill_obj, microbit_image_fill); + +mp_obj_t microbit_image_blit(mp_uint_t n_args, const mp_obj_t *args) { + microbit_image_obj_t *self = (microbit_image_obj_t*)args[0]; + check_mutability(self); + + mp_obj_t src = args[1]; + if (mp_obj_get_type(src) != µbit_image_type) { + nlr_raise(mp_obj_new_exception_msg(&mp_type_TypeError, "expecting an image")); + } + if (n_args == 7) { + nlr_raise(mp_obj_new_exception_msg(&mp_type_TypeError, + "must specify both offsets")); + } + mp_int_t x = mp_obj_get_int(args[2]); + mp_int_t y = mp_obj_get_int(args[3]); + mp_int_t w = mp_obj_get_int(args[4]); + mp_int_t h = mp_obj_get_int(args[5]); + if (w < 0 || h < 0) { + nlr_raise(mp_obj_new_exception_msg(&mp_type_ValueError, + "size cannot be negative")); + } + mp_int_t xdest; + mp_int_t ydest; + if (n_args == 6) { + xdest = 0; + ydest = 0; + } else { + xdest = mp_obj_get_int(args[6]); + ydest = mp_obj_get_int(args[7]); + } + image_blit((microbit_image_obj_t *)src, &(self->greyscale), x, y, w, h, xdest, ydest); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(microbit_image_blit_obj, 6, 8, microbit_image_blit); + +mp_obj_t microbit_image_crop(mp_uint_t n_args, const mp_obj_t *args) { + (void)n_args; + microbit_image_obj_t *self = (microbit_image_obj_t*)args[0]; + mp_int_t x0 = mp_obj_get_int(args[1]); + mp_int_t y0 = mp_obj_get_int(args[2]); + mp_int_t x1 = mp_obj_get_int(args[3]); + mp_int_t y1 = mp_obj_get_int(args[4]); + return image_crop(self, x0, y0, x1, y1); +} +MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(microbit_image_crop_obj, 5, 5, microbit_image_crop); + +mp_obj_t microbit_image_shift_left(mp_obj_t self_in, mp_obj_t n_in) { + microbit_image_obj_t *self = (microbit_image_obj_t*)self_in; + mp_int_t n = mp_obj_get_int(n_in); + return image_shift(self, n, 0); +} +MP_DEFINE_CONST_FUN_OBJ_2(microbit_image_shift_left_obj, microbit_image_shift_left); + +mp_obj_t microbit_image_shift_right(mp_obj_t self_in, mp_obj_t n_in) { + microbit_image_obj_t *self = (microbit_image_obj_t*)self_in; + mp_int_t n = mp_obj_get_int(n_in); + return image_shift(self, -n, 0); +} +MP_DEFINE_CONST_FUN_OBJ_2(microbit_image_shift_right_obj, microbit_image_shift_right); + +mp_obj_t microbit_image_shift_up(mp_obj_t self_in, mp_obj_t n_in) { + microbit_image_obj_t *self = (microbit_image_obj_t*)self_in; + mp_int_t n = mp_obj_get_int(n_in); + return image_shift(self, 0, n); +} +MP_DEFINE_CONST_FUN_OBJ_2(microbit_image_shift_up_obj, microbit_image_shift_up); + +mp_obj_t microbit_image_shift_down(mp_obj_t self_in, mp_obj_t n_in) { + microbit_image_obj_t *self = (microbit_image_obj_t*)self_in; + mp_int_t n = mp_obj_get_int(n_in); + return image_shift(self, 0, -n); +} +MP_DEFINE_CONST_FUN_OBJ_2(microbit_image_shift_down_obj, microbit_image_shift_down); + +mp_obj_t microbit_image_copy(mp_obj_t self_in) { + microbit_image_obj_t *self = (microbit_image_obj_t*)self_in; + return self->copy(); +} +MP_DEFINE_CONST_FUN_OBJ_1(microbit_image_copy_obj, microbit_image_copy); + +mp_obj_t microbit_image_invert(mp_obj_t self_in) { + microbit_image_obj_t *self = (microbit_image_obj_t*)self_in; + return self->invert(); +} +MP_DEFINE_CONST_FUN_OBJ_1(microbit_image_invert_obj, microbit_image_invert); + + +STATIC const mp_map_elem_t microbit_image_locals_dict_table[] = { + { MP_OBJ_NEW_QSTR(MP_QSTR_width), (mp_obj_t)µbit_image_width_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_height), (mp_obj_t)µbit_image_height_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_get_pixel), (mp_obj_t)µbit_image_get_pixel_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_set_pixel), (mp_obj_t)µbit_image_set_pixel_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_shift_left), (mp_obj_t)µbit_image_shift_left_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_shift_right), (mp_obj_t)µbit_image_shift_right_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_shift_up), (mp_obj_t)µbit_image_shift_up_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_shift_down), (mp_obj_t)µbit_image_shift_down_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_copy), (mp_obj_t)µbit_image_copy_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_crop), (mp_obj_t)µbit_image_crop_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_invert), (mp_obj_t)µbit_image_invert_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_fill), (mp_obj_t)µbit_image_fill_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_blit), (mp_obj_t)µbit_image_blit_obj }, + + { MP_OBJ_NEW_QSTR(MP_QSTR_HEART), (mp_obj_t)µbit_const_image_heart_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_HEART_SMALL), (mp_obj_t)µbit_const_image_heart_small_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_HAPPY), (mp_obj_t)µbit_const_image_happy_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_SMILE), (mp_obj_t)µbit_const_image_smile_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_SAD), (mp_obj_t)µbit_const_image_sad_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_CONFUSED), (mp_obj_t)µbit_const_image_confused_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_ANGRY), (mp_obj_t)µbit_const_image_angry_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_ASLEEP), (mp_obj_t)µbit_const_image_asleep_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_SURPRISED), (mp_obj_t)µbit_const_image_surprised_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_SILLY), (mp_obj_t)µbit_const_image_silly_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_FABULOUS), (mp_obj_t)µbit_const_image_fabulous_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_MEH), (mp_obj_t)µbit_const_image_meh_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_YES), (mp_obj_t)µbit_const_image_yes_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_NO), (mp_obj_t)µbit_const_image_no_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_CLOCK12), (mp_obj_t)µbit_const_image_clock12_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_CLOCK1), (mp_obj_t)µbit_const_image_clock1_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_CLOCK2), (mp_obj_t)µbit_const_image_clock2_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_CLOCK3), (mp_obj_t)µbit_const_image_clock3_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_CLOCK4), (mp_obj_t)µbit_const_image_clock4_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_CLOCK5), (mp_obj_t)µbit_const_image_clock5_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_CLOCK6), (mp_obj_t)µbit_const_image_clock6_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_CLOCK7), (mp_obj_t)µbit_const_image_clock7_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_CLOCK8), (mp_obj_t)µbit_const_image_clock8_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_CLOCK9), (mp_obj_t)µbit_const_image_clock9_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_CLOCK10), (mp_obj_t)µbit_const_image_clock10_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_CLOCK11), (mp_obj_t)µbit_const_image_clock11_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_ARROW_N), (mp_obj_t)µbit_const_image_arrow_n_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_ARROW_NE), (mp_obj_t)µbit_const_image_arrow_ne_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_ARROW_E), (mp_obj_t)µbit_const_image_arrow_e_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_ARROW_SE), (mp_obj_t)µbit_const_image_arrow_se_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_ARROW_S), (mp_obj_t)µbit_const_image_arrow_s_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_ARROW_SW), (mp_obj_t)µbit_const_image_arrow_sw_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_ARROW_W), (mp_obj_t)µbit_const_image_arrow_w_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_ARROW_NW), (mp_obj_t)µbit_const_image_arrow_nw_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_TRIANGLE), (mp_obj_t)µbit_const_image_triangle_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_TRIANGLE_LEFT), (mp_obj_t)µbit_const_image_triangle_left_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_CHESSBOARD), (mp_obj_t)µbit_const_image_chessboard_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_DIAMOND), (mp_obj_t)µbit_const_image_diamond_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_DIAMOND_SMALL), (mp_obj_t)µbit_const_image_diamond_small_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_SQUARE), (mp_obj_t)µbit_const_image_square_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_SQUARE_SMALL), (mp_obj_t)µbit_const_image_square_small_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_RABBIT), (mp_obj_t)µbit_const_image_rabbit }, + { MP_OBJ_NEW_QSTR(MP_QSTR_COW), (mp_obj_t)µbit_const_image_cow }, + { MP_OBJ_NEW_QSTR(MP_QSTR_MUSIC_CROTCHET), (mp_obj_t)µbit_const_image_music_crotchet_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_MUSIC_QUAVER), (mp_obj_t)µbit_const_image_music_quaver_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_MUSIC_QUAVERS), (mp_obj_t)µbit_const_image_music_quavers_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_PITCHFORK), (mp_obj_t)µbit_const_image_pitchfork_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_XMAS), (mp_obj_t)µbit_const_image_xmas_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_PACMAN), (mp_obj_t)µbit_const_image_pacman_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_TARGET), (mp_obj_t)µbit_const_image_target_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_ALL_CLOCKS), (mp_obj_t)µbit_const_image_all_clocks_tuple_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_ALL_ARROWS), (mp_obj_t)µbit_const_image_all_arrows_tuple_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_TSHIRT), (mp_obj_t)µbit_const_image_tshirt_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_ROLLERSKATE), (mp_obj_t)µbit_const_image_rollerskate_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_DUCK), (mp_obj_t)µbit_const_image_duck_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_HOUSE), (mp_obj_t)µbit_const_image_house_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_TORTOISE), (mp_obj_t)µbit_const_image_tortoise_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_BUTTERFLY), (mp_obj_t)µbit_const_image_butterfly_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_STICKFIGURE), (mp_obj_t)µbit_const_image_stickfigure_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_GHOST), (mp_obj_t)µbit_const_image_ghost_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_SWORD), (mp_obj_t)µbit_const_image_sword_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_GIRAFFE), (mp_obj_t)µbit_const_image_giraffe_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_SKULL), (mp_obj_t)µbit_const_image_skull_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_UMBRELLA), (mp_obj_t)µbit_const_image_umbrella_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_SNAKE), (mp_obj_t)µbit_const_image_snake_obj }, +}; + +STATIC MP_DEFINE_CONST_DICT(microbit_image_locals_dict, microbit_image_locals_dict_table); + +#define THE_FONT MicroBitFont::defaultFont + +#define ASCII_START 32 +#define ASCII_END 126 + +STATIC const unsigned char *get_font_data_from_char(char c) { + if (c < ASCII_START || c > ASCII_END) { + c = '?'; + } + int offset = (c-ASCII_START) * 5; + return THE_FONT + offset; +} + +STATIC mp_int_t get_pixel_from_font_data(const unsigned char *data, int x, int y) { + /* The following logic belongs in MicroBitFont */ + return ((data[y]>>(4-x))&1); +} + +void microbit_image_set_from_char(greyscale_t *img, char c) { + const unsigned char *data = get_font_data_from_char(c); + for (int x = 0; x < 5; ++x) { + for (int y = 0; y < 5; ++y) { + img->setPixelValue(x, y, get_pixel_from_font_data(data, x, y)*MAX_BRIGHTNESS); + } + } +} + + +microbit_image_obj_t *microbit_image_for_char(char c) { + greyscale_t *result = greyscale_new(5,5); + microbit_image_set_from_char(result, c); + return (microbit_image_obj_t *)result; +} + +microbit_image_obj_t *microbit_image_dim(microbit_image_obj_t *lhs, mp_float_t fval) { + if (fval < 0) + nlr_raise(mp_obj_new_exception_msg(&mp_type_ValueError, "Brightness multiplier must not be negative.")); + greyscale_t *result = greyscale_new(lhs->width(), lhs->height()); + for (int x = 0; x < lhs->width(); ++x) { + for (int y = 0; y < lhs->width(); ++y) { + int val = min((int)lhs->getPixelValue(x,y)*fval+0.5, MAX_BRIGHTNESS); + result->setPixelValue(x, y, val); + } + } + return (microbit_image_obj_t *)result; +} + +microbit_image_obj_t *microbit_image_sum(microbit_image_obj_t *lhs, microbit_image_obj_t *rhs, bool add) { + mp_int_t h = lhs->height(); + mp_int_t w = lhs->width(); + if (rhs->height() != h || lhs->width() != w) { + nlr_raise(mp_obj_new_exception_msg(&mp_type_ValueError, "Images must be the same size.")); + } + greyscale_t *result = greyscale_new(w, h); + for (int x = 0; x < w; ++x) { + for (int y = 0; y < h; ++y) { + int val; + int lval = lhs->getPixelValue(x,y); + int rval = rhs->getPixelValue(x,y); + if (add) + val = min(lval + rval, MAX_BRIGHTNESS); + else + val = max(0, lval - rval); + result->setPixelValue(x, y, val); + } + } + return (microbit_image_obj_t *)result; +} + +STATIC mp_obj_t image_binary_op(mp_uint_t op, mp_obj_t lhs_in, mp_obj_t rhs_in) { + if (mp_obj_get_type(lhs_in) != µbit_image_type) { + return MP_OBJ_NULL; // op not supported + } + microbit_image_obj_t *lhs = (microbit_image_obj_t *)lhs_in; + switch(op) { + case MP_BINARY_OP_ADD: + case MP_BINARY_OP_SUBTRACT: + break; + case MP_BINARY_OP_MULTIPLY: + return microbit_image_dim(lhs, mp_obj_get_float(rhs_in)); + case MP_BINARY_OP_TRUE_DIVIDE: + return microbit_image_dim(lhs, 1.0/mp_obj_get_float(rhs_in)); + default: + return MP_OBJ_NULL; // op not supported + } + if (mp_obj_get_type(rhs_in) != µbit_image_type) { + return MP_OBJ_NULL; // op not supported + } + return microbit_image_sum(lhs, (microbit_image_obj_t *)rhs_in, op == MP_BINARY_OP_ADD); +} + + +const mp_obj_type_t microbit_image_type = { + { &mp_type_type }, + .name = MP_QSTR_MicroBitImage, + .print = microbit_image_print, + .make_new = microbit_image_make_new, + .call = NULL, + .unary_op = NULL, + .binary_op = image_binary_op, + .attr = NULL, + .subscr = NULL, + .getiter = NULL, + .iternext = NULL, + .buffer_p = {NULL}, + .stream_p = NULL, + .bases_tuple = NULL, + .locals_dict = (mp_obj_dict_t*)µbit_image_locals_dict, +}; + +typedef struct _scrolling_string_t { + mp_obj_base_t base; + char const *str; + mp_uint_t len; + mp_obj_t ref; + bool monospace; + bool repeat; +} scrolling_string_t; + +typedef struct _scrolling_string_iterator_t { + mp_obj_base_t base; + mp_obj_t ref; + greyscale_t *img; + char const *next_char; + char const *start; + char const *end; + uint8_t offset; + uint8_t offset_limit; + bool monospace; + bool repeat; + char right; +} scrolling_string_iterator_t; + +extern const mp_obj_type_t microbit_scrolling_string_type; +extern const mp_obj_type_t microbit_scrolling_string_iterator_type; + +mp_obj_t scrolling_string_image_iterable(const char* str, mp_uint_t len, mp_obj_t ref, bool monospace, bool repeat) { + scrolling_string_t *result = m_new_obj(scrolling_string_t); + result->base.type = µbit_scrolling_string_type; + result->str = str; + result->len = len; + result->ref = ref; + result->monospace = monospace; + result->repeat = repeat; + return result; +} + +STATIC int font_column_non_blank(const unsigned char *font_data, unsigned int col) { + for (int y = 0; y < 5; ++y) { + if (get_pixel_from_font_data(font_data, col, y)) { + return 1; + } + } + return 0; +} + +/* Not strictly the rightmost non-blank column, but the rightmost in columns 2,3 or 4. */ +STATIC unsigned int rightmost_non_blank_column(const unsigned char *font_data) { + if (font_column_non_blank(font_data, 4)) { + return 4; + } + if (font_column_non_blank(font_data, 3)) { + return 3; + } + return 2; +} + +static void restart(scrolling_string_iterator_t *iter) { + iter->next_char = iter->start; + iter->offset = 0; + if (iter->start < iter->end) { + iter->right = *iter->next_char; + if (iter->monospace) { + iter->offset_limit = 5; + } else { + iter->offset_limit = rightmost_non_blank_column(get_font_data_from_char(iter->right)) + 1; + } + } else { + iter->right = ' '; + iter->offset_limit = 5; + } +} + +STATIC mp_obj_t get_microbit_scrolling_string_iter(mp_obj_t o_in) { + scrolling_string_t *str = (scrolling_string_t *)o_in; + scrolling_string_iterator_t *result = m_new_obj(scrolling_string_iterator_t); + result->base.type = µbit_scrolling_string_iterator_type; + result->img = greyscale_new(5,5); + result->start = str->str; + result->ref = str->ref; + result->monospace = str->monospace; + result->end = result->start + str->len; + result->repeat = str->repeat; + restart(result); + return result; +} + +STATIC mp_obj_t microbit_scrolling_string_iter_next(mp_obj_t o_in) { + scrolling_string_iterator_t *iter = (scrolling_string_iterator_t *)o_in; + if (iter->next_char == iter->end && iter->offset == 5) { + if (iter->repeat) { + restart(iter); + iter->img->clear(); + } else { + return MP_OBJ_STOP_ITERATION; + } + } + for (int x = 0; x < 4; x++) { + for (int y = 0; y < 5; y++) { + iter->img->setPixelValue(x, y, iter->img->getPixelValue(x+1, y)); + } + } + for (int y = 0; y < 5; y++) { + iter->img->setPixelValue(4, y, 0); + } + const unsigned char *font_data; + if (iter->offset < iter->offset_limit) { + font_data = get_font_data_from_char(iter->right); + for (int y = 0; y < 5; ++y) { + int pix = get_pixel_from_font_data(font_data, iter->offset, y)*MAX_BRIGHTNESS; + iter->img->setPixelValue(4, y, pix); + } + } else if (iter->offset == iter->offset_limit) { + ++iter->next_char; + if (iter->next_char == iter->end) { + iter->right = ' '; + iter->offset_limit = 5; + iter->offset = 0; + } else { + iter->right = *iter->next_char; + font_data = get_font_data_from_char(iter->right); + if (iter->monospace) { + iter->offset = -1; + iter->offset_limit = 5; + } else { + iter->offset = -font_column_non_blank(font_data, 0); + iter->offset_limit = rightmost_non_blank_column(font_data)+1; + } + } + } + ++iter->offset; + return iter->img; +} + +const mp_obj_type_t microbit_scrolling_string_type = { + { &mp_type_type }, + .name = MP_QSTR_ScrollingString, + .print = NULL, + .make_new = NULL, + .call = NULL, + .unary_op = NULL, + .binary_op = NULL, + .attr = NULL, + .subscr = NULL, + .getiter = get_microbit_scrolling_string_iter, + .iternext = NULL, + .buffer_p = {NULL}, + .stream_p = NULL, + .bases_tuple = NULL, + .locals_dict = NULL, +}; + +const mp_obj_type_t microbit_scrolling_string_iterator_type = { + { &mp_type_type }, + .name = MP_QSTR_iterator, + .print = NULL, + .make_new = NULL, + .call = NULL, + .unary_op = NULL, + .binary_op = NULL, + .attr = NULL, + .subscr = NULL, + .getiter = mp_identity, + .iternext = microbit_scrolling_string_iter_next, + .buffer_p = {NULL}, + .stream_p = NULL, + .bases_tuple = NULL, + .locals_dict = NULL, +}; + +/** Facade types to present a string as a sequence of images. + * These are necessary to avoid allocation during iteration, + * which may happen in interrupt handlers. + */ + +typedef struct _string_image_facade_t { + mp_obj_base_t base; + mp_obj_t string; + greyscale_t *image; +} string_image_facade_t; + +static mp_obj_t string_image_facade_subscr(mp_obj_t self_in, mp_obj_t index_in, mp_obj_t value) { + if (value == MP_OBJ_SENTINEL) { + // Fill in image + string_image_facade_t *self = (string_image_facade_t *)self_in; + mp_uint_t len; + const char *text = mp_obj_str_get_data(self->string, &len); + mp_uint_t index = mp_get_index(self->base.type, len, index_in, false); + microbit_image_set_from_char(self->image, text[index]); + return self->image; + } else { + return MP_OBJ_NULL; // op not supported + } +} + +static mp_obj_t facade_unary_op(mp_uint_t op, mp_obj_t self_in) { + string_image_facade_t *self = (string_image_facade_t *)self_in; + switch (op) { + case MP_UNARY_OP_LEN: + return mp_obj_len(self->string); + default: return MP_OBJ_NULL; // op not supported + } +} + +static mp_obj_t microbit_facade_iterator(mp_obj_t iterable); + +const mp_obj_type_t string_image_facade_type = { + { &mp_type_type }, + .name = MP_QSTR_Facade, + .print = NULL, + .make_new = NULL, + .call = NULL, + .unary_op = facade_unary_op, + .binary_op = NULL, + .attr = NULL, + .subscr = string_image_facade_subscr, + .getiter = microbit_facade_iterator, + .iternext = NULL, + .buffer_p = {NULL}, + .stream_p = NULL, + .bases_tuple = NULL, + NULL +}; + + +typedef struct _facade_iterator_t { + mp_obj_base_t base; + mp_obj_t string; + mp_uint_t index; + greyscale_t *image; +} facade_iterator_t; + +mp_obj_t microbit_string_facade(mp_obj_t string) { + string_image_facade_t *result = m_new_obj(string_image_facade_t); + result->base.type = &string_image_facade_type; + result->string = string; + result->image = greyscale_new(5,5); + return result; +} + +static mp_obj_t microbit_facade_iter_next(mp_obj_t iter_in) { + facade_iterator_t *iter = (facade_iterator_t *)iter_in; + mp_uint_t len; + const char *text = mp_obj_str_get_data(iter->string, &len); + if (iter->index >= len) { + return MP_OBJ_STOP_ITERATION; + } + microbit_image_set_from_char(iter->image, text[iter->index]); + iter->index++; + return iter->image; +} + +const mp_obj_type_t microbit_facade_iterator_type = { + { &mp_type_type }, + .name = MP_QSTR_iterator, + .print = NULL, + .make_new = NULL, + .call = NULL, + .unary_op = NULL, + .binary_op = NULL, + .attr = NULL, + .subscr = NULL, + .getiter = mp_identity, + .iternext = microbit_facade_iter_next, + .buffer_p = {NULL}, + .stream_p = NULL, + .bases_tuple = NULL, + NULL +}; + +mp_obj_t microbit_facade_iterator(mp_obj_t iterable_in) { + facade_iterator_t *result = m_new_obj(facade_iterator_t); + string_image_facade_t *iterable = (string_image_facade_t *)iterable_in; + result->base.type = µbit_facade_iterator_type; + result->string = iterable->string; + result->image = iterable->image; + result->index = 0; + return result; +} + +} diff --git a/ports/nrf/boards/microbit/modules/microbitimage.h b/ports/nrf/boards/microbit/modules/microbitimage.h new file mode 100644 index 0000000000..94d167dae8 --- /dev/null +++ b/ports/nrf/boards/microbit/modules/microbitimage.h @@ -0,0 +1,92 @@ +#ifndef __MICROPY_INCLUDED_MICROBIT_IMAGE_H__ +#define __MICROPY_INCLUDED_MICROBIT_IMAGE_H__ + +#include "py/runtime.h" + +#define MAX_BRIGHTNESS 9 + +/** Monochrome images are immutable, which means that + * we only need one bit per pixel which saves quite a lot + * of memory */ + +/* we reserve a couple of bits, so we won't need to modify the + * layout if we need to add more functionality or subtypes. */ +#define TYPE_AND_FLAGS \ + mp_obj_base_t base; \ + uint8_t five:1; \ + uint8_t reserved1:1; \ + uint8_t reserved2:1 + +typedef struct _image_base_t { + TYPE_AND_FLAGS; +} image_base_t; + +typedef struct _monochrome_5by5_t { + TYPE_AND_FLAGS; + uint8_t pixel44: 1; + uint8_t bits24[3]; + + /* This is an internal method it is up to the caller to validate the inputs */ + uint8_t getPixelValue(mp_int_t x, mp_int_t y); + +} monochrome_5by5_t; + +typedef struct _greyscale_t { + TYPE_AND_FLAGS; + uint8_t height; + uint8_t width; + uint8_t byte_data[]; /* Static initializer for this will have to be C, not C++ */ + void clear(); + + /* Thiese are internal methods and it is up to the caller to validate the inputs */ + uint8_t getPixelValue(mp_int_t x, mp_int_t y); + void setPixelValue(mp_int_t x, mp_int_t y, mp_int_t val); + void fill(mp_int_t val); +} greyscale_t; + +typedef union _microbit_image_obj_t { + image_base_t base; + monochrome_5by5_t monochrome_5by5; + greyscale_t greyscale; + + mp_int_t height(); + mp_int_t width(); + greyscale_t *copy(); + greyscale_t *invert(); + + /* This is an internal method it is up to the caller to validate the inputs */ + uint8_t getPixelValue(mp_int_t x, mp_int_t y); + +} microbit_image_obj_t; + +/** Return a facade object that presents the string as a sequence of images */ +mp_obj_t microbit_string_facade(mp_obj_t string); + +void microbit_image_set_from_char(greyscale_t *img, char c); +microbit_image_obj_t *microbit_image_for_char(char c); +mp_obj_t microbit_image_slice(microbit_image_obj_t *img, mp_int_t start, mp_int_t width, mp_int_t stride); +/* ref exists so that we can pull a string out of an object and not have it GC'ed while oterating over it */ +mp_obj_t scrolling_string_image_iterable(const char* str, mp_uint_t len, mp_obj_t ref, bool monospace, bool repeat); + +#define SMALL_IMAGE(p0, p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12, p13, p14, p15, p16, p17, p18, p19, p20, p21, p22, p23, p44) \ +{ \ + { µbit_image_type }, \ + 1, 0, 0, (p44), \ + { \ + (p0)|((p1)<<1)|((p2)<<2)|((p3)<<3)|((p4)<<4)|((p5)<<5)|((p6)<<6)|((p7)<<7), \ + (p8)|((p9)<<1)|((p10)<<2)|((p11)<<3)|((p12)<<4)|((p13)<<5)|((p14)<<6)|((p15)<<7), \ + (p16)|((p17)<<1)|((p18)<<2)|((p19)<<3)|((p20)<<4)|((p21)<<5)|((p22)<<6)|((p23)<<7) \ + } \ +} + +extern const monochrome_5by5_t microbit_blank_image; +extern const monochrome_5by5_t microbit_const_image_heart_obj; + +#define BLANK_IMAGE (microbit_image_obj_t *)(µbit_blank_image) +#define HEART_IMAGE (microbit_image_obj_t *)(µbit_const_image_heart_obj) +#define HAPPY_IMAGE (microbit_image_obj_t *)(µbit_const_image_happy_obj) + +microbit_image_obj_t *microbit_image_dim(microbit_image_obj_t *lhs, mp_float_t fval); +microbit_image_obj_t *microbit_image_sum(microbit_image_obj_t *lhs, microbit_image_obj_t *rhs, bool add); + +#endif // __MICROPY_INCLUDED_MICROBIT_IMAGE_H__ diff --git a/ports/nrf/boards/microbit/modules/modmicrobit.cpp b/ports/nrf/boards/microbit/modules/modmicrobit.cpp new file mode 100644 index 0000000000..7201790cf5 --- /dev/null +++ b/ports/nrf/boards/microbit/modules/modmicrobit.cpp @@ -0,0 +1,158 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2015 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 + * 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 "mbed.h" + +extern "C" { + +#include "py/nlr.h" +#include "py/obj.h" +#include "py/mphal.h" +#include "modmicrobit.h" +#include "microbitdisplay.h" +#include "microbitimage.h" + +extern uint32_t ticks; + +STATIC mp_obj_t microbit_reset_(void) { + NVIC_SystemReset(); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_0(microbit_reset_obj, microbit_reset_); + +STATIC mp_obj_t microbit_sleep(mp_obj_t ms_in) { + mp_int_t ms; + if (mp_obj_is_integer(ms_in)) { + ms = mp_obj_get_int(ms_in); + } else { + ms = (mp_int_t)mp_obj_get_float(ms_in); + } + if (ms > 0) { + mp_hal_delay_ms(ms); + } + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_1(microbit_sleep_obj, microbit_sleep); + +STATIC mp_obj_t microbit_running_time(void) { + return MP_OBJ_NEW_SMALL_INT(ticks); +} +MP_DEFINE_CONST_FUN_OBJ_0(microbit_running_time_obj, microbit_running_time); + +static const monochrome_5by5_t panic = SMALL_IMAGE( + 1,1,0,1,1, + 1,1,0,1,1, + 0,0,0,0,0, + 0,1,1,1,0, + 1,0,0,0,1 +); + +STATIC mp_obj_t microbit_panic(mp_uint_t n_args, const mp_obj_t *args) { + while(true) { + microbit_display_show(µbit_display_obj, (microbit_image_obj_t*)&panic); + mp_hal_delay_ms(1000); + char num[4]; + int code; + if (n_args) { + code = mp_obj_get_int(args[0]); + } else { + code = 0; + } + num[2] = code%10 + '0'; + code /= 10; + num[1] = code%10 + '0'; + code /= 10; + num[0] = code%10 + '0'; + for (int i = 0; i < 3; i++) { + microbit_display_show(µbit_display_obj, microbit_image_for_char(num[i])); + mp_hal_delay_ms(1000); + } + } + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(microbit_panic_obj, 0, 1, microbit_panic); + +STATIC mp_obj_t microbit_temperature(void) { + int temp; + NRF_TEMP->TASKS_START = 1; + while (NRF_TEMP->EVENTS_DATARDY == 0); + NRF_TEMP->EVENTS_DATARDY = 0; + temp = NRF_TEMP->TEMP; + NRF_TEMP->TASKS_STOP = 1; + return mp_obj_new_float(temp/4.0); +} +MP_DEFINE_CONST_FUN_OBJ_0(microbit_temperature_obj, microbit_temperature); + +STATIC const mp_map_elem_t microbit_module_globals_table[] = { + { MP_OBJ_NEW_QSTR(MP_QSTR___name__), MP_OBJ_NEW_QSTR(MP_QSTR_microbit) }, + + { MP_OBJ_NEW_QSTR(MP_QSTR_Image), (mp_obj_t)µbit_image_type }, + + { MP_OBJ_NEW_QSTR(MP_QSTR_display), (mp_obj_t)µbit_display_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_button_a), (mp_obj_t)µbit_button_a_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_button_b), (mp_obj_t)µbit_button_b_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_accelerometer), (mp_obj_t)µbit_accelerometer_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_compass), (mp_obj_t)µbit_compass_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_i2c), (mp_obj_t)µbit_i2c_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_uart), (mp_obj_t)µbit_uart_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_spi), (mp_obj_t)µbit_spi_obj }, + + { MP_OBJ_NEW_QSTR(MP_QSTR_reset), (mp_obj_t)µbit_reset_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_sleep), (mp_obj_t)µbit_sleep_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_running_time), (mp_obj_t)µbit_running_time_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_panic), (mp_obj_t)µbit_panic_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_temperature), (mp_obj_t)µbit_temperature_obj }, + + { MP_OBJ_NEW_QSTR(MP_QSTR_pin0), (mp_obj_t)µbit_p0_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_pin1), (mp_obj_t)µbit_p1_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_pin2), (mp_obj_t)µbit_p2_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_pin3), (mp_obj_t)µbit_p3_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_pin4), (mp_obj_t)µbit_p4_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_pin5), (mp_obj_t)µbit_p5_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_pin6), (mp_obj_t)µbit_p6_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_pin7), (mp_obj_t)µbit_p7_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_pin8), (mp_obj_t)µbit_p8_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_pin9), (mp_obj_t)µbit_p9_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_pin10), (mp_obj_t)µbit_p10_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_pin11), (mp_obj_t)µbit_p11_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_pin12), (mp_obj_t)µbit_p12_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_pin13), (mp_obj_t)µbit_p13_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_pin14), (mp_obj_t)µbit_p14_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_pin15), (mp_obj_t)µbit_p15_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_pin16), (mp_obj_t)µbit_p16_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_pin19), (mp_obj_t)µbit_p19_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_pin20), (mp_obj_t)µbit_p20_obj }, +}; + +STATIC MP_DEFINE_CONST_DICT(microbit_module_globals, microbit_module_globals_table); + +const mp_obj_module_t microbit_module = { + .base = { &mp_type_module }, + .name = MP_QSTR_microbit, + .globals = (mp_obj_dict_t*)µbit_module_globals, +}; + +} diff --git a/ports/nrf/boards/microbit/modules/modmicrobit.h b/ports/nrf/boards/microbit/modules/modmicrobit.h new file mode 100644 index 0000000000..722bf1c1ac --- /dev/null +++ b/ports/nrf/boards/microbit/modules/modmicrobit.h @@ -0,0 +1,234 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2015 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 + * 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_MICROBIT_MODMICROBIT_H__ +#define __MICROPY_INCLUDED_MICROBIT_MODMICROBIT_H__ + +#include "py/objtuple.h" + +extern const mp_obj_type_t microbit_ad_pin_type; +extern const mp_obj_type_t microbit_dig_pin_type; +extern const mp_obj_type_t microbit_touch_pin_type; + +extern const struct _microbit_pin_obj_t microbit_p0_obj; +extern const struct _microbit_pin_obj_t microbit_p1_obj; +extern const struct _microbit_pin_obj_t microbit_p2_obj; +extern const struct _microbit_pin_obj_t microbit_p3_obj; +extern const struct _microbit_pin_obj_t microbit_p4_obj; +extern const struct _microbit_pin_obj_t microbit_p5_obj; +extern const struct _microbit_pin_obj_t microbit_p6_obj; +extern const struct _microbit_pin_obj_t microbit_p7_obj; +extern const struct _microbit_pin_obj_t microbit_p8_obj; +extern const struct _microbit_pin_obj_t microbit_p9_obj; +extern const struct _microbit_pin_obj_t microbit_p10_obj; +extern const struct _microbit_pin_obj_t microbit_p11_obj; +extern const struct _microbit_pin_obj_t microbit_p12_obj; +extern const struct _microbit_pin_obj_t microbit_p13_obj; +extern const struct _microbit_pin_obj_t microbit_p14_obj; +extern const struct _microbit_pin_obj_t microbit_p15_obj; +extern const struct _microbit_pin_obj_t microbit_p16_obj; +extern const struct _microbit_pin_obj_t microbit_p19_obj; +extern const struct _microbit_pin_obj_t microbit_p20_obj; + +extern const mp_obj_type_t microbit_const_image_type; +extern const struct _monochrome_5by5_t microbit_const_image_heart_obj; +extern const struct _monochrome_5by5_t microbit_const_image_heart_small_obj; +extern const struct _monochrome_5by5_t microbit_const_image_happy_obj; +extern const struct _monochrome_5by5_t microbit_const_image_smile_obj; +extern const struct _monochrome_5by5_t microbit_const_image_sad_obj; +extern const struct _monochrome_5by5_t microbit_const_image_confused_obj; +extern const struct _monochrome_5by5_t microbit_const_image_angry_obj; +extern const struct _monochrome_5by5_t microbit_const_image_asleep_obj; +extern const struct _monochrome_5by5_t microbit_const_image_surprised_obj; +extern const struct _monochrome_5by5_t microbit_const_image_silly_obj; +extern const struct _monochrome_5by5_t microbit_const_image_fabulous_obj; +extern const struct _monochrome_5by5_t microbit_const_image_meh_obj; +extern const struct _monochrome_5by5_t microbit_const_image_yes_obj; +extern const struct _monochrome_5by5_t microbit_const_image_no_obj; +extern const struct _monochrome_5by5_t microbit_const_image_clock12_obj; +extern const struct _monochrome_5by5_t microbit_const_image_clock1_obj; +extern const struct _monochrome_5by5_t microbit_const_image_clock2_obj; +extern const struct _monochrome_5by5_t microbit_const_image_clock3_obj; +extern const struct _monochrome_5by5_t microbit_const_image_clock4_obj; +extern const struct _monochrome_5by5_t microbit_const_image_clock5_obj; +extern const struct _monochrome_5by5_t microbit_const_image_clock6_obj; +extern const struct _monochrome_5by5_t microbit_const_image_clock7_obj; +extern const struct _monochrome_5by5_t microbit_const_image_clock8_obj; +extern const struct _monochrome_5by5_t microbit_const_image_clock9_obj; +extern const struct _monochrome_5by5_t microbit_const_image_clock10_obj; +extern const struct _monochrome_5by5_t microbit_const_image_clock11_obj; +extern const struct _monochrome_5by5_t microbit_const_image_arrow_n_obj; +extern const struct _monochrome_5by5_t microbit_const_image_arrow_ne_obj; +extern const struct _monochrome_5by5_t microbit_const_image_arrow_e_obj; +extern const struct _monochrome_5by5_t microbit_const_image_arrow_se_obj; +extern const struct _monochrome_5by5_t microbit_const_image_arrow_s_obj; +extern const struct _monochrome_5by5_t microbit_const_image_arrow_sw_obj; +extern const struct _monochrome_5by5_t microbit_const_image_arrow_w_obj; +extern const struct _monochrome_5by5_t microbit_const_image_arrow_nw_obj; +extern const struct _monochrome_5by5_t microbit_const_image_triangle_obj; +extern const struct _monochrome_5by5_t microbit_const_image_triangle_left_obj; +extern const struct _monochrome_5by5_t microbit_const_image_chessboard_obj; +extern const struct _monochrome_5by5_t microbit_const_image_diamond_obj; +extern const struct _monochrome_5by5_t microbit_const_image_diamond_small_obj; +extern const struct _monochrome_5by5_t microbit_const_image_square_obj; +extern const struct _monochrome_5by5_t microbit_const_image_square_small_obj; +extern const struct _monochrome_5by5_t microbit_const_image_rabbit; +extern const struct _monochrome_5by5_t microbit_const_image_cow; +extern const struct _monochrome_5by5_t microbit_const_image_music_crotchet_obj; +extern const struct _monochrome_5by5_t microbit_const_image_music_quaver_obj; +extern const struct _monochrome_5by5_t microbit_const_image_music_quavers_obj; +extern const struct _monochrome_5by5_t microbit_const_image_pitchfork_obj; +extern const struct _monochrome_5by5_t microbit_const_image_xmas_obj; +extern const struct _monochrome_5by5_t microbit_const_image_pacman_obj; +extern const struct _monochrome_5by5_t microbit_const_image_target_obj; +extern const struct _mp_obj_tuple_t microbit_const_image_all_clocks_tuple_obj; +extern const struct _mp_obj_tuple_t microbit_const_image_all_arrows_tuple_obj; +extern const struct _monochrome_5by5_t microbit_const_image_tshirt_obj; +extern const struct _monochrome_5by5_t microbit_const_image_rollerskate_obj; +extern const struct _monochrome_5by5_t microbit_const_image_duck_obj; +extern const struct _monochrome_5by5_t microbit_const_image_house_obj; +extern const struct _monochrome_5by5_t microbit_const_image_tortoise_obj; +extern const struct _monochrome_5by5_t microbit_const_image_butterfly_obj; +extern const struct _monochrome_5by5_t microbit_const_image_stickfigure_obj; +extern const struct _monochrome_5by5_t microbit_const_image_ghost_obj; +extern const struct _monochrome_5by5_t microbit_const_image_sword_obj; +extern const struct _monochrome_5by5_t microbit_const_image_giraffe_obj; +extern const struct _monochrome_5by5_t microbit_const_image_skull_obj; +extern const struct _monochrome_5by5_t microbit_const_image_umbrella_obj; +extern const struct _monochrome_5by5_t microbit_const_image_snake_obj; + +extern const struct _mp_obj_tuple_t microbit_music_tune_dadadadum_obj; +extern const struct _mp_obj_tuple_t microbit_music_tune_entertainer_obj; +extern const struct _mp_obj_tuple_t microbit_music_tune_prelude_obj; +extern const struct _mp_obj_tuple_t microbit_music_tune_ode_obj; +extern const struct _mp_obj_tuple_t microbit_music_tune_nyan_obj; +extern const struct _mp_obj_tuple_t microbit_music_tune_ringtone_obj; +extern const struct _mp_obj_tuple_t microbit_music_tune_funk_obj; +extern const struct _mp_obj_tuple_t microbit_music_tune_blues_obj; +extern const struct _mp_obj_tuple_t microbit_music_tune_birthday_obj; +extern const struct _mp_obj_tuple_t microbit_music_tune_wedding_obj; +extern const struct _mp_obj_tuple_t microbit_music_tune_funeral_obj; +extern const struct _mp_obj_tuple_t microbit_music_tune_punchline_obj; +extern const struct _mp_obj_tuple_t microbit_music_tune_python_obj; +extern const struct _mp_obj_tuple_t microbit_music_tune_baddy_obj; +extern const struct _mp_obj_tuple_t microbit_music_tune_chase_obj; +extern const struct _mp_obj_tuple_t microbit_music_tune_ba_ding_obj; +extern const struct _mp_obj_tuple_t microbit_music_tune_wawawawaa_obj; +extern const struct _mp_obj_tuple_t microbit_music_tune_jump_up_obj; +extern const struct _mp_obj_tuple_t microbit_music_tune_jump_down_obj; +extern const struct _mp_obj_tuple_t microbit_music_tune_power_up_obj; +extern const struct _mp_obj_tuple_t microbit_music_tune_power_down_obj; + +extern const mp_obj_type_t microbit_image_type; + +extern const mp_obj_type_t microbit_accelerometer_type; +extern const struct _microbit_accelerometer_obj_t microbit_accelerometer_obj; + +extern struct _microbit_display_obj_t microbit_display_obj; +extern const struct _microbit_button_obj_t microbit_button_a_obj; +extern const struct _microbit_button_obj_t microbit_button_b_obj; +extern const struct _microbit_compass_obj_t microbit_compass_obj; +extern const struct _microbit_i2c_obj_t microbit_i2c_obj; +extern struct _microbit_uart_obj_t microbit_uart_obj; +extern struct _microbit_spi_obj_t microbit_spi_obj; + +MP_DECLARE_CONST_FUN_OBJ(microbit_reset_obj); +MP_DECLARE_CONST_FUN_OBJ(microbit_sleep_obj); +MP_DECLARE_CONST_FUN_OBJ(microbit_random_obj); +MP_DECLARE_CONST_FUN_OBJ(microbit_running_time_obj); +MP_DECLARE_CONST_FUN_OBJ(microbit_temperature_obj); +MP_DECLARE_CONST_FUN_OBJ(microbit_panic_obj); +MP_DECLARE_CONST_FUN_OBJ(microbit_accelerometer_get_x_obj); +MP_DECLARE_CONST_FUN_OBJ(microbit_accelerometer_get_y_obj); +MP_DECLARE_CONST_FUN_OBJ(microbit_accelerometer_get_z_obj); +MP_DECLARE_CONST_FUN_OBJ(microbit_button_is_pressed_obj); +MP_DECLARE_CONST_FUN_OBJ(microbit_button_was_pressed_obj); +MP_DECLARE_CONST_FUN_OBJ(microbit_button_get_presses_obj); +MP_DECLARE_CONST_FUN_OBJ(microbit_compass_is_calibrated_obj); +MP_DECLARE_CONST_FUN_OBJ(microbit_compass_heading_obj); +MP_DECLARE_CONST_FUN_OBJ(microbit_compass_calibrate_obj); +MP_DECLARE_CONST_FUN_OBJ(microbit_compass_is_calibrating_obj); +MP_DECLARE_CONST_FUN_OBJ(microbit_compass_clear_calibration_obj); +MP_DECLARE_CONST_FUN_OBJ(microbit_compass_get_x_obj); +MP_DECLARE_CONST_FUN_OBJ(microbit_compass_get_y_obj); +MP_DECLARE_CONST_FUN_OBJ(microbit_compass_get_z_obj); +MP_DECLARE_CONST_FUN_OBJ(microbit_compass_get_field_strength_obj); +MP_DECLARE_CONST_FUN_OBJ(microbit_display_show_obj); +MP_DECLARE_CONST_FUN_OBJ(microbit_display_scroll_obj); +MP_DECLARE_CONST_FUN_OBJ(microbit_display_clear_obj); +MP_DECLARE_CONST_FUN_OBJ(microbit_display_get_pixel_obj); +MP_DECLARE_CONST_FUN_OBJ(microbit_display_set_pixel_obj); +MP_DECLARE_CONST_FUN_OBJ(microbit_display_on_obj); +MP_DECLARE_CONST_FUN_OBJ(microbit_display_off_obj); +MP_DECLARE_CONST_FUN_OBJ(microbit_display_is_on_obj); +MP_DECLARE_CONST_FUN_OBJ(microbit_pin_read_digital_obj); +MP_DECLARE_CONST_FUN_OBJ(microbit_pin_write_digital_obj); +MP_DECLARE_CONST_FUN_OBJ(microbit_pin_read_analog_obj); +MP_DECLARE_CONST_FUN_OBJ(microbit_pin_write_analog_obj); +MP_DECLARE_CONST_FUN_OBJ(microbit_pin_is_touched_obj); +MP_DECLARE_CONST_FUN_OBJ(microbit_pin_set_analog_period_obj); +MP_DECLARE_CONST_FUN_OBJ(microbit_pin_set_analog_period_microseconds_obj); +MP_DECLARE_CONST_FUN_OBJ(microbit_i2c_init_obj); +MP_DECLARE_CONST_FUN_OBJ(microbit_i2c_read_obj); +MP_DECLARE_CONST_FUN_OBJ(microbit_i2c_write_obj); +MP_DECLARE_CONST_FUN_OBJ(microbit_image_width_obj); +MP_DECLARE_CONST_FUN_OBJ(microbit_image_height_obj); +MP_DECLARE_CONST_FUN_OBJ(microbit_image_get_pixel_obj); +MP_DECLARE_CONST_FUN_OBJ(microbit_image_set_pixel_obj); +MP_DECLARE_CONST_FUN_OBJ(microbit_image_shift_left_obj); +MP_DECLARE_CONST_FUN_OBJ(microbit_image_shift_right_obj); +MP_DECLARE_CONST_FUN_OBJ(microbit_image_shift_up_obj); +MP_DECLARE_CONST_FUN_OBJ(microbit_image_shift_down_obj); +MP_DECLARE_CONST_FUN_OBJ(microbit_image_copy_obj); +MP_DECLARE_CONST_FUN_OBJ(microbit_image_crop_obj); +MP_DECLARE_CONST_FUN_OBJ(microbit_image_invert_obj); +MP_DECLARE_CONST_FUN_OBJ(microbit_image_slice_obj); +MP_DECLARE_CONST_FUN_OBJ(microbit_uart_init_obj); +MP_DECLARE_CONST_FUN_OBJ(microbit_uart_any_obj); +MP_DECLARE_CONST_FUN_OBJ(mp_stream_read_obj); +MP_DECLARE_CONST_FUN_OBJ(mp_stream_readall_obj); +MP_DECLARE_CONST_FUN_OBJ(mp_stream_unbuffered_readline_obj); +MP_DECLARE_CONST_FUN_OBJ(mp_stream_readinto_obj); +MP_DECLARE_CONST_FUN_OBJ(mp_stream_write_obj); +MP_DECLARE_CONST_FUN_OBJ(microbit_spi_init_obj); +MP_DECLARE_CONST_FUN_OBJ(microbit_spi_write_obj); +MP_DECLARE_CONST_FUN_OBJ(microbit_spi_read_obj); +MP_DECLARE_CONST_FUN_OBJ(microbit_spi_write_readinto_obj); +MP_DECLARE_CONST_FUN_OBJ(microbit_music_set_tempo_obj); +MP_DECLARE_CONST_FUN_OBJ(microbit_music_pitch_obj); +MP_DECLARE_CONST_FUN_OBJ(microbit_music_play_obj); +MP_DECLARE_CONST_FUN_OBJ(microbit_music_get_tempo_obj); +MP_DECLARE_CONST_FUN_OBJ(microbit_music_stop_obj); +MP_DECLARE_CONST_FUN_OBJ(microbit_music_reset_obj); +MP_DECLARE_CONST_FUN_OBJ(love_badaboom_obj); +MP_DECLARE_CONST_FUN_OBJ(this_authors_obj); + +extern const mp_obj_module_t microbit_module; +extern const mp_obj_module_t music_module; +extern const mp_obj_module_t love_module; +extern const mp_obj_module_t antigravity_module; +extern const mp_obj_module_t this_module; + +#endif // __MICROPY_INCLUDED_MICROBIT_MODMICROBIT_H__