microbitdisplay.c 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576
  1. /*
  2. * This file is part of the Micro Python project, http://micropython.org/
  3. *
  4. * The MIT License (MIT)
  5. *
  6. * Copyright (c) 2015 Damien P. George
  7. *
  8. * Permission is hereby granted, free of charge, to any person obtaining a copy
  9. * of this software and associated documentation files (the "Software"), to deal
  10. * in the Software without restriction, including without limitation the rights
  11. * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  12. * copies of the Software, and to permit persons to whom the Software is
  13. * furnished to do so, subject to the following conditions:
  14. *
  15. * The above copyright notice and this permission notice shall be included in
  16. * all copies or substantial portions of the Software.
  17. *
  18. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  19. * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  20. * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  21. * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  22. * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  23. * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  24. * THE SOFTWARE.
  25. */
  26. #include <string.h>
  27. #include "py/obj.h"
  28. #include "py/runtime.h"
  29. #include "py/gc.h"
  30. #include "nrf_gpio.h"
  31. #include "microbitimage.h"
  32. #include "microbitdisplay.h"
  33. #include "iters.h"
  34. #include "ticker.h"
  35. #define min(a,b) (((a)<(b))?(a):(b))
  36. void microbit_display_show(microbit_display_obj_t *display, microbit_image_obj_t *image) {
  37. mp_int_t w = min(imageWidth(image), 5);
  38. mp_int_t h = min(imageHeight(image), 5);
  39. mp_int_t x = 0;
  40. mp_int_t brightnesses = 0;
  41. for (; x < w; ++x) {
  42. mp_int_t y = 0;
  43. for (; y < h; ++y) {
  44. uint8_t pix = imageGetPixelValue(image, x, y);
  45. display->image_buffer[x][y] = pix;
  46. brightnesses |= (1 << pix);
  47. }
  48. for (; y < 5; ++y) {
  49. display->image_buffer[x][y] = 0;
  50. }
  51. }
  52. for (; x < 5; ++x) {
  53. for (mp_int_t y = 0; y < 5; ++y) {
  54. display->image_buffer[x][y] = 0;
  55. }
  56. }
  57. display->brightnesses = brightnesses;
  58. }
  59. #define DEFAULT_PRINT_SPEED 400
  60. mp_obj_t microbit_display_show_func(mp_uint_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
  61. // Cancel any animations.
  62. MP_STATE_PORT(async_data)[0] = NULL;
  63. MP_STATE_PORT(async_data)[1] = NULL;
  64. static const mp_arg_t show_allowed_args[] = {
  65. { MP_QSTR_image, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} },
  66. { MP_QSTR_delay, MP_ARG_INT, {.u_int = DEFAULT_PRINT_SPEED} },
  67. { MP_QSTR_clear, MP_ARG_KW_ONLY | MP_ARG_BOOL, {.u_bool = false} },
  68. { MP_QSTR_wait, MP_ARG_KW_ONLY | MP_ARG_BOOL, {.u_bool = true} },
  69. { MP_QSTR_loop, MP_ARG_KW_ONLY | MP_ARG_BOOL, {.u_bool = false} },
  70. };
  71. // Parse the args.
  72. microbit_display_obj_t *self = (microbit_display_obj_t*)pos_args[0];
  73. mp_arg_val_t args[MP_ARRAY_SIZE(show_allowed_args)];
  74. mp_arg_parse_all(n_args - 1, pos_args + 1, kw_args, MP_ARRAY_SIZE(show_allowed_args), show_allowed_args, args);
  75. mp_obj_t image = args[0].u_obj;
  76. mp_int_t delay = args[1].u_int;
  77. bool clear = args[2].u_bool;
  78. bool wait = args[3].u_bool;
  79. bool loop = args[4].u_bool;
  80. if (MP_OBJ_IS_STR(image)) {
  81. // arg is a string object
  82. mp_uint_t len;
  83. const char *str = mp_obj_str_get_data(image, &len);
  84. if (len == 0) {
  85. // There are no chars; do nothing.
  86. return mp_const_none;
  87. } else if (len == 1) {
  88. if (!clear && !loop) {
  89. // A single char; convert to an image and print that.
  90. image = microbit_image_for_char(str[0]);
  91. goto single_image_immediate;
  92. }
  93. }
  94. image = microbit_string_facade(image);
  95. } else if (mp_obj_get_type(image) == &microbit_image_type) {
  96. if (!clear && !loop) {
  97. goto single_image_immediate;
  98. }
  99. image = mp_obj_new_tuple(1, &image);
  100. }
  101. // iterable:
  102. if (args[4].u_bool) { /*loop*/
  103. image = microbit_repeat_iterator(image);
  104. }
  105. microbit_display_animate(self, image, delay, clear, wait);
  106. return mp_const_none;
  107. single_image_immediate:
  108. microbit_display_show(self, (microbit_image_obj_t *)image);
  109. return mp_const_none;
  110. }
  111. MP_DEFINE_CONST_FUN_OBJ_KW(microbit_display_show_obj, 1, microbit_display_show_func);
  112. static uint8_t async_mode;
  113. static mp_obj_t async_iterator = NULL;
  114. // Record if an error occurs in async animation. Unfortunately there is no way to report this.
  115. static volatile bool wakeup_event = false;
  116. static mp_uint_t async_delay = 1000;
  117. static mp_uint_t async_tick = 0;
  118. static bool async_clear = false;
  119. bool microbit_display_active_animation(void) {
  120. return async_mode == ASYNC_MODE_ANIMATION;
  121. }
  122. STATIC void async_stop(void) {
  123. async_iterator = NULL;
  124. async_mode = ASYNC_MODE_STOPPED;
  125. async_tick = 0;
  126. async_delay = 1000;
  127. async_clear = false;
  128. MP_STATE_PORT(async_data)[0] = NULL;
  129. MP_STATE_PORT(async_data)[1] = NULL;
  130. wakeup_event = true;
  131. }
  132. STATIC void wait_for_event(void) {
  133. while (!wakeup_event) {
  134. // allow CTRL-C to stop the animation
  135. if (MP_STATE_VM(mp_pending_exception) != MP_OBJ_NULL) {
  136. async_stop();
  137. return;
  138. }
  139. __WFI();
  140. }
  141. wakeup_event = false;
  142. }
  143. typedef struct {
  144. uint8_t x;
  145. uint8_t y;
  146. } DisplayPoint;
  147. #define NO_CONN 0
  148. #define ROW_COUNT 3
  149. #define COLUMN_COUNT 9
  150. static const DisplayPoint display_map[COLUMN_COUNT][ROW_COUNT] = {
  151. {{0,0}, {4,2}, {2,4}},
  152. {{2,0}, {0,2}, {4,4}},
  153. {{4,0}, {2,2}, {0,4}},
  154. {{4,3}, {1,0}, {0,1}},
  155. {{3,3}, {3,0}, {1,1}},
  156. {{2,3}, {3,4}, {2,1}},
  157. {{1,3}, {1,4}, {3,1}},
  158. {{0,3}, {NO_CONN,NO_CONN}, {4,1}},
  159. {{1,2}, {NO_CONN,NO_CONN}, {3,2}}
  160. };
  161. #define MIN_COLUMN_PIN 4
  162. #define COLUMN_PINS_MASK 0x1ff0
  163. #define MIN_ROW_PIN 13
  164. #define MAX_ROW_PIN 15
  165. #define ROW_PINS_MASK 0xe000
  166. static inline void displaySetPinsForRow(microbit_display_obj_t * p_display, uint8_t brightness) {
  167. if (brightness == 0) {
  168. nrf_gpio_port_out_clear(NRF_GPIO, COLUMN_PINS_MASK & ~p_display->pins_for_brightness[brightness]);
  169. } else {
  170. nrf_gpio_pin_set(p_display->pins_for_brightness[brightness]);
  171. }
  172. }
  173. /* This is the primary PWM driver/display driver. It will operate on one row
  174. * (9 pins) per invocation. It will turn on LEDs with maximum brightness,
  175. * then let the "callback" callback turn off the LEDs as appropriate for the
  176. * required brightness level.
  177. *
  178. * For each row
  179. * Turn off all the LEDs in the previous row
  180. * Set the column bits high (off)
  181. * Set the row strobe low (off)
  182. * Turn on all the LEDs in the current row that have maximum brightness
  183. * Set the row strobe high (on)
  184. * Set some/all column bits low (on)
  185. * Register the PWM callback
  186. * For each callback start with brightness 0
  187. * If brightness 0
  188. * Turn off the LEDs specified at this level
  189. * Else
  190. * Turn on the LEDs specified at this level
  191. * If brightness max
  192. * Disable the PWM callback
  193. * Else
  194. * Re-queue the PWM callback after the appropriate delay
  195. */
  196. static void displayAdvanceRow(microbit_display_obj_t * p_display) {
  197. /* Clear all of the column bits */
  198. nrf_gpio_port_out_set(NRF_GPIO, COLUMN_PINS_MASK);
  199. /* Clear the strobe bit for this row */
  200. nrf_gpio_pin_clear(p_display->strobe_row + MIN_ROW_PIN);
  201. /* Move to the next row. Before this, "this row" refers to the row
  202. * manipulated by the previous invocation of this function. After this,
  203. * "this row" refers to the row manipulated by the current invocation of
  204. * this function. */
  205. p_display->strobe_row++;
  206. // Reset the row counts and bit mask when we have hit the max.
  207. if (p_display->strobe_row == ROW_COUNT) {
  208. p_display->strobe_row = 0;
  209. }
  210. // Set pin for this row.
  211. // Prepare row for rendering.
  212. for (int i = 0; i <= MAX_BRIGHTNESS; i++) {
  213. p_display->pins_for_brightness[i] = 0;
  214. }
  215. for (int i = 0; i < COLUMN_COUNT; i++) {
  216. int x = display_map[i][p_display->strobe_row].x;
  217. int y = display_map[i][p_display->strobe_row].y;
  218. int brightness = microbit_display_obj.image_buffer[x][y];
  219. p_display->pins_for_brightness[brightness] |= (1<<(i+MIN_COLUMN_PIN));
  220. (void)brightness;
  221. }
  222. /* Enable the strobe bit for this row */
  223. nrf_gpio_pin_set(p_display->strobe_row + MIN_ROW_PIN);
  224. /* Enable the column bits for all pins that need to be on. */
  225. nrf_gpio_port_out_clear(NRF_GPIO, p_display->pins_for_brightness[MAX_BRIGHTNESS]);
  226. }
  227. static const uint16_t render_timings[] =
  228. // The scale is (approximately) exponential,
  229. // each step is approx x1.9 greater than the previous.
  230. { 0, // Bright, Ticks Duration, Relative power
  231. 2, // 1, 2, 32µs, inf
  232. 2, // 2, 4, 64µs, 200%
  233. 4, // 3, 8, 128µs, 200%
  234. 7, // 4, 15, 240µs, 187%
  235. 13, // 5, 28, 448µs, 187%
  236. 25, // 6, 53, 848µs, 189%
  237. 49, // 7, 102, 1632µs, 192%
  238. 97, // 8, 199, 3184µs, 195%
  239. // Always on 9, 375, 6000µs, 188%
  240. };
  241. #define DISPLAY_TICKER_SLOT 1
  242. /* This is the PWM callback. It is registered by the animation callback and
  243. * will unregister itself when all of the brightness steps are complete. */
  244. static int32_t callback(void) {
  245. microbit_display_obj_t *display = &microbit_display_obj;
  246. mp_uint_t brightness = display->previous_brightness;
  247. displaySetPinsForRow(display, brightness);
  248. brightness += 1;
  249. if (brightness == MAX_BRIGHTNESS) {
  250. clear_ticker_callback(DISPLAY_TICKER_SLOT);
  251. return -1;
  252. }
  253. display->previous_brightness = brightness;
  254. // Return interval (in 16µs ticks) until next callback
  255. return render_timings[brightness];
  256. }
  257. static void draw_object(mp_obj_t obj) {
  258. microbit_display_obj_t *display = (microbit_display_obj_t*)MP_STATE_PORT(async_data)[0];
  259. if (obj == MP_OBJ_STOP_ITERATION) {
  260. if (async_clear) {
  261. microbit_display_show(&microbit_display_obj, BLANK_IMAGE);
  262. async_clear = false;
  263. } else {
  264. async_stop();
  265. }
  266. } else if (mp_obj_get_type(obj) == &microbit_image_type) {
  267. microbit_display_show(display, (microbit_image_obj_t *)obj);
  268. } else if (MP_OBJ_IS_STR(obj)) {
  269. mp_uint_t len;
  270. const char *str = mp_obj_str_get_data(obj, &len);
  271. if (len == 1) {
  272. microbit_display_show(display, microbit_image_for_char(str[0]));
  273. } else {
  274. async_stop();
  275. }
  276. } else {
  277. MP_STATE_VM(mp_pending_exception) = mp_obj_new_exception_msg(&mp_type_TypeError, "not an image.");
  278. async_stop();
  279. }
  280. }
  281. static void microbit_display_update(void) {
  282. async_tick += MILLISECONDS_PER_MACRO_TICK;
  283. if (async_tick < async_delay) {
  284. return;
  285. }
  286. async_tick = 0;
  287. switch (async_mode) {
  288. case ASYNC_MODE_ANIMATION:
  289. {
  290. if (MP_STATE_PORT(async_data)[0] == NULL || MP_STATE_PORT(async_data)[1] == NULL) {
  291. async_stop();
  292. break;
  293. }
  294. /* WARNING: We are executing in an interrupt handler.
  295. * If an exception is raised here then we must hand it to the VM. */
  296. mp_obj_t obj;
  297. nlr_buf_t nlr;
  298. gc_lock();
  299. if (nlr_push(&nlr) == 0) {
  300. obj = mp_iternext_allow_raise(async_iterator);
  301. nlr_pop();
  302. gc_unlock();
  303. } else {
  304. gc_unlock();
  305. if (!mp_obj_is_subclass_fast(MP_OBJ_FROM_PTR(((mp_obj_base_t*)nlr.ret_val)->type),
  306. MP_OBJ_FROM_PTR(&mp_type_StopIteration))) {
  307. // An exception other than StopIteration, so set it for the VM to raise later
  308. // If memory error, write an appropriate message.
  309. if (mp_obj_get_type(nlr.ret_val) == &mp_type_MemoryError) {
  310. mp_printf(&mp_plat_print, "Allocation in interrupt handler");
  311. }
  312. MP_STATE_VM(mp_pending_exception) = MP_OBJ_FROM_PTR(nlr.ret_val);
  313. }
  314. obj = MP_OBJ_STOP_ITERATION;
  315. }
  316. draw_object(obj);
  317. break;
  318. }
  319. case ASYNC_MODE_CLEAR:
  320. microbit_display_show(&microbit_display_obj, BLANK_IMAGE);
  321. async_stop();
  322. break;
  323. }
  324. }
  325. #define GREYSCALE_MASK ((1<<MAX_BRIGHTNESS)-2)
  326. /* This is the top-level animation/display registered callback. */
  327. void microbit_display_tick(void) {
  328. /* Do nothing if the display is not active. */
  329. if (!microbit_display_obj.active) {
  330. return;
  331. }
  332. displayAdvanceRow(&microbit_display_obj);
  333. microbit_display_update();
  334. microbit_display_obj.previous_brightness = 0;
  335. if (microbit_display_obj.brightnesses & GREYSCALE_MASK) {
  336. set_ticker_callback(DISPLAY_TICKER_SLOT, callback, 1800);
  337. }
  338. }
  339. void microbit_display_animate(microbit_display_obj_t *self, mp_obj_t iterable, mp_int_t delay, bool clear, bool wait) {
  340. // Reset the repeat state.
  341. MP_STATE_PORT(async_data)[0] = NULL;
  342. MP_STATE_PORT(async_data)[1] = NULL;
  343. async_iterator = mp_getiter(iterable, NULL);
  344. async_delay = delay;
  345. async_clear = clear;
  346. MP_STATE_PORT(async_data)[0] = self; // so it doesn't get GC'd
  347. MP_STATE_PORT(async_data)[1] = async_iterator;
  348. wakeup_event = false;
  349. mp_obj_t obj = mp_iternext_allow_raise(async_iterator);
  350. draw_object(obj);
  351. async_tick = 0;
  352. async_mode = ASYNC_MODE_ANIMATION;
  353. if (wait) {
  354. wait_for_event();
  355. }
  356. }
  357. // Delay in ms in between moving display one column to the left.
  358. #define DEFAULT_SCROLL_SPEED 150
  359. void microbit_display_scroll(microbit_display_obj_t *self, const char* str, bool wait) {
  360. mp_obj_t iterable = scrolling_string_image_iterable(str, strlen(str), NULL, false, false);
  361. microbit_display_animate(self, iterable, DEFAULT_SCROLL_SPEED, false, wait);
  362. }
  363. mp_obj_t microbit_display_scroll_func(mp_uint_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
  364. static const mp_arg_t scroll_allowed_args[] = {
  365. { MP_QSTR_text, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} },
  366. { MP_QSTR_delay, MP_ARG_INT, {.u_int = DEFAULT_SCROLL_SPEED} },
  367. { MP_QSTR_wait, MP_ARG_KW_ONLY | MP_ARG_BOOL, {.u_bool = true} },
  368. { MP_QSTR_monospace, MP_ARG_KW_ONLY | MP_ARG_BOOL, {.u_bool = false} },
  369. { MP_QSTR_loop, MP_ARG_KW_ONLY | MP_ARG_BOOL, {.u_bool = false} },
  370. };
  371. // Parse the args.
  372. microbit_display_obj_t *self = (microbit_display_obj_t*)pos_args[0];
  373. mp_arg_val_t args[MP_ARRAY_SIZE(scroll_allowed_args)];
  374. mp_arg_parse_all(n_args - 1, pos_args + 1, kw_args, MP_ARRAY_SIZE(scroll_allowed_args), scroll_allowed_args, args);
  375. mp_uint_t len;
  376. const char* str = mp_obj_str_get_data(args[0].u_obj, &len);
  377. mp_obj_t iterable = scrolling_string_image_iterable(str, len, args[0].u_obj, args[3].u_bool /*monospace?*/, args[4].u_bool /*loop*/);
  378. microbit_display_animate(self, iterable, args[1].u_int /*delay*/, false/*clear*/, args[2].u_bool/*wait?*/);
  379. return mp_const_none;
  380. }
  381. MP_DEFINE_CONST_FUN_OBJ_KW(microbit_display_scroll_obj, 1, microbit_display_scroll_func);
  382. mp_obj_t microbit_display_on_func(mp_obj_t obj) {
  383. microbit_display_obj_t *self = (microbit_display_obj_t*)obj;
  384. /* Try to reclaim the pins we need */
  385. /*
  386. microbit_obj_pin_fail_if_cant_acquire(&microbit_p3_obj);
  387. microbit_obj_pin_fail_if_cant_acquire(&microbit_p4_obj);
  388. microbit_obj_pin_fail_if_cant_acquire(&microbit_p6_obj);
  389. microbit_obj_pin_fail_if_cant_acquire(&microbit_p7_obj);
  390. microbit_obj_pin_fail_if_cant_acquire(&microbit_p9_obj);
  391. microbit_obj_pin_fail_if_cant_acquire(&microbit_p10_obj);
  392. microbit_obj_pin_acquire(&microbit_p3_obj, microbit_pin_mode_display);
  393. microbit_obj_pin_acquire(&microbit_p4_obj, microbit_pin_mode_display);
  394. microbit_obj_pin_acquire(&microbit_p6_obj, microbit_pin_mode_display);
  395. microbit_obj_pin_acquire(&microbit_p7_obj, microbit_pin_mode_display);
  396. microbit_obj_pin_acquire(&microbit_p9_obj, microbit_pin_mode_display);
  397. microbit_obj_pin_acquire(&microbit_p10_obj, microbit_pin_mode_display);
  398. */
  399. /* Make sure all pins are in the correct state */
  400. microbit_display_init();
  401. /* Re-enable the display loop. This will resume any animations in
  402. * progress and display any static image. */
  403. self->active = true;
  404. return mp_const_none;
  405. }
  406. MP_DEFINE_CONST_FUN_OBJ_1(microbit_display_on_obj, microbit_display_on_func);
  407. mp_obj_t microbit_display_off_func(mp_obj_t obj) {
  408. microbit_display_obj_t *self = (microbit_display_obj_t*)obj;
  409. /* Disable the display loop. This will pause any animations in progress.
  410. * It will not prevent a user from attempting to modify the state, but
  411. * modifications will not appear to have any effect until the display loop
  412. * is re-enabled. */
  413. self->active = false;
  414. /* Disable the row strobes, allowing the columns to be used freely for
  415. * GPIO. */
  416. nrf_gpio_port_out_clear(0, ROW_PINS_MASK);
  417. /* Free pins for other uses */
  418. /*
  419. microbit_obj_pin_free(&microbit_p3_obj);
  420. microbit_obj_pin_free(&microbit_p4_obj);
  421. microbit_obj_pin_free(&microbit_p6_obj);
  422. microbit_obj_pin_free(&microbit_p7_obj);
  423. microbit_obj_pin_free(&microbit_p9_obj);
  424. microbit_obj_pin_free(&microbit_p10_obj);
  425. */
  426. return mp_const_none;
  427. }
  428. MP_DEFINE_CONST_FUN_OBJ_1(microbit_display_off_obj, microbit_display_off_func);
  429. mp_obj_t microbit_display_is_on_func(mp_obj_t obj) {
  430. microbit_display_obj_t *self = (microbit_display_obj_t*)obj;
  431. if (self->active) {
  432. return mp_const_true;
  433. }
  434. else {
  435. return mp_const_false;
  436. }
  437. }
  438. MP_DEFINE_CONST_FUN_OBJ_1(microbit_display_is_on_obj, microbit_display_is_on_func);
  439. void microbit_display_clear(void) {
  440. // Reset repeat state, cancel animation and clear screen.
  441. wakeup_event = false;
  442. async_mode = ASYNC_MODE_CLEAR;
  443. async_tick = async_delay - MILLISECONDS_PER_MACRO_TICK;
  444. wait_for_event();
  445. }
  446. mp_obj_t microbit_display_clear_func(mp_obj_t self_in) {
  447. microbit_display_clear();
  448. return mp_const_none;
  449. }
  450. MP_DEFINE_CONST_FUN_OBJ_1(microbit_display_clear_obj, microbit_display_clear_func);
  451. void microbit_display_set_pixel(microbit_display_obj_t *display, mp_int_t x, mp_int_t y, mp_int_t bright) {
  452. if (x < 0 || y < 0 || x > 4 || y > 4) {
  453. mp_raise_ValueError("index out of bounds.");
  454. }
  455. if (bright < 0 || bright > MAX_BRIGHTNESS) {
  456. mp_raise_ValueError("brightness out of bounds.");
  457. }
  458. display->image_buffer[x][y] = bright;
  459. display->brightnesses |= (1 << bright);
  460. }
  461. STATIC mp_obj_t microbit_display_set_pixel_func(mp_uint_t n_args, const mp_obj_t *args) {
  462. (void)n_args;
  463. microbit_display_obj_t *self = (microbit_display_obj_t*)args[0];
  464. microbit_display_set_pixel(self, mp_obj_get_int(args[1]), mp_obj_get_int(args[2]), mp_obj_get_int(args[3]));
  465. return mp_const_none;
  466. }
  467. MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(microbit_display_set_pixel_obj, 4, 4, microbit_display_set_pixel_func);
  468. mp_int_t microbit_display_get_pixel(microbit_display_obj_t *display, mp_int_t x, mp_int_t y) {
  469. if (x < 0 || y < 0 || x > 4 || y > 4) {
  470. mp_raise_ValueError("index out of bounds.");
  471. }
  472. return display->image_buffer[x][y];
  473. }
  474. STATIC mp_obj_t microbit_display_get_pixel_func(mp_obj_t self_in, mp_obj_t x_in, mp_obj_t y_in) {
  475. microbit_display_obj_t *self = (microbit_display_obj_t*)self_in;
  476. return MP_OBJ_NEW_SMALL_INT(microbit_display_get_pixel(self, mp_obj_get_int(x_in), mp_obj_get_int(y_in)));
  477. }
  478. MP_DEFINE_CONST_FUN_OBJ_3(microbit_display_get_pixel_obj, microbit_display_get_pixel_func);
  479. STATIC const mp_rom_map_elem_t microbit_display_locals_dict_table[] = {
  480. { MP_ROM_QSTR(MP_QSTR_get_pixel), MP_ROM_PTR(&microbit_display_get_pixel_obj) },
  481. { MP_ROM_QSTR(MP_QSTR_set_pixel), MP_ROM_PTR(&microbit_display_set_pixel_obj) },
  482. { MP_ROM_QSTR(MP_QSTR_show), MP_ROM_PTR(&microbit_display_show_obj) },
  483. { MP_ROM_QSTR(MP_QSTR_scroll), MP_ROM_PTR(&microbit_display_scroll_obj) },
  484. { MP_ROM_QSTR(MP_QSTR_clear), MP_ROM_PTR(&microbit_display_clear_obj) },
  485. { MP_ROM_QSTR(MP_QSTR_on), MP_ROM_PTR(&microbit_display_on_obj) },
  486. { MP_ROM_QSTR(MP_QSTR_off), MP_ROM_PTR(&microbit_display_off_obj) },
  487. { MP_ROM_QSTR(MP_QSTR_is_on), MP_ROM_PTR(&microbit_display_is_on_obj) },
  488. };
  489. STATIC MP_DEFINE_CONST_DICT(microbit_display_locals_dict, microbit_display_locals_dict_table);
  490. const mp_obj_type_t microbit_display_type = {
  491. { &mp_type_type },
  492. .name = MP_QSTR_MicroBitDisplay,
  493. .print = NULL,
  494. .make_new = NULL,
  495. .call = NULL,
  496. .unary_op = NULL,
  497. .binary_op = NULL,
  498. .attr = NULL,
  499. .subscr = NULL,
  500. .getiter = NULL,
  501. .iternext = NULL,
  502. .buffer_p = {NULL},
  503. .locals_dict = (mp_obj_dict_t*)&microbit_display_locals_dict,
  504. };
  505. microbit_display_obj_t microbit_display_obj = {
  506. {&microbit_display_type},
  507. {{ 0, }},
  508. .previous_brightness = 0,
  509. .active = 1,
  510. .strobe_row = 0,
  511. .brightnesses = 0,
  512. .pins_for_brightness = { 0 },
  513. };
  514. void microbit_display_init(void) {
  515. // Set pins as output.
  516. for (int i = MIN_COLUMN_PIN; i <= MAX_ROW_PIN; i++) {
  517. nrf_gpio_cfg_output(i);
  518. }
  519. }