From 3eb42336e68938b580567f54ecbf836e39611ec1 Mon Sep 17 00:00:00 2001 From: Phil Howard Date: Mon, 25 Jul 2022 11:36:15 +0100 Subject: [PATCH] Pico Unicorn: Refactor into class. Borrow heavily from Galactic/Cosmic Unicorn for the PIO/chained-DMA setup. --- examples/pico_unicorn/demo.cpp | 3 - libraries/pico_unicorn/README.md | 16 +- libraries/pico_unicorn/pico_unicorn.cpp | 210 ++++++++++-------- libraries/pico_unicorn/pico_unicorn.hpp | 22 +- micropython/examples/pico_unicorn/demo.py | 4 +- micropython/examples/pico_unicorn/rainbow.py | 4 +- .../modules/pico_unicorn/pico_unicorn.c | 55 +++-- .../modules/pico_unicorn/pico_unicorn.cpp | 152 ++++++------- .../modules/pico_unicorn/pico_unicorn.h | 20 +- 9 files changed, 264 insertions(+), 222 deletions(-) diff --git a/examples/pico_unicorn/demo.cpp b/examples/pico_unicorn/demo.cpp index 3ec174fb..0e5f67c7 100644 --- a/examples/pico_unicorn/demo.cpp +++ b/examples/pico_unicorn/demo.cpp @@ -10,9 +10,6 @@ using namespace pimoroni; PicoUnicorn pico_unicorn; int main() { - - pico_unicorn.init(); - bool a_pressed = false; bool b_pressed = false; bool x_pressed = false; diff --git a/libraries/pico_unicorn/README.md b/libraries/pico_unicorn/README.md index c2d80406..29ffc16b 100644 --- a/libraries/pico_unicorn/README.md +++ b/libraries/pico_unicorn/README.md @@ -12,13 +12,12 @@ We've included helper functions to handle every aspect of drawing to the display - [Buttons](#buttons) - [WIDTH / HEIGHT](#width--height) - [Functions](#functions) - - [init](#init) - - [set_pixel](#set_pixel) - - [is_pressed](#is_pressed) + - [set\_pixel](#set_pixel) + - [is\_pressed](#is_pressed) ## Example Program -The following example sets up Pico Unicorn, displays some basic demo text and graphics and will illuminate the RGB LED green if the A button is presse +The following example sets up Pico Unicorn, displays some basic demo text and graphics and will illuminate the RGB LED green if the A button is pressed. ```c++ @@ -48,15 +47,6 @@ int num_pixels = pico_unicorn.WIDTH * pico_unicorn.HEIGHT; ### Functions -#### init - -Sets up Pico Unicorn. `init` must be called before any other functions since it configures the PIO and require GPIO inputs. Just call `init()` like so: - -```c++ -PicoUnicorn pico_unicorn; -pico_unicorn.init(); -``` - #### set_pixel ```c++ diff --git a/libraries/pico_unicorn/pico_unicorn.cpp b/libraries/pico_unicorn/pico_unicorn.cpp index 4548ee0d..39576a4d 100644 --- a/libraries/pico_unicorn/pico_unicorn.cpp +++ b/libraries/pico_unicorn/pico_unicorn.cpp @@ -44,82 +44,53 @@ enum pin { Y = 15, }; -constexpr uint32_t ROW_COUNT = 7; -constexpr uint32_t ROW_BYTES = 12; -constexpr uint32_t BCD_FRAMES = 15; // includes fet discharge frame -constexpr uint32_t BITSTREAM_LENGTH = (ROW_COUNT * ROW_BYTES * BCD_FRAMES); - -// must be aligned for 32bit dma transfer -alignas(4) static uint8_t bitstream[BITSTREAM_LENGTH] = {0}; - static uint32_t dma_channel; - -static inline void unicorn_jetpack_program_init(PIO pio, uint sm, uint offset) { - pio_gpio_init(pio, pin::LED_DATA); - pio_gpio_init(pio, pin::LED_CLOCK); - pio_gpio_init(pio, pin::LED_LATCH); - pio_gpio_init(pio, pin::LED_BLANK); - pio_gpio_init(pio, pin::ROW_0); - pio_gpio_init(pio, pin::ROW_1); - pio_gpio_init(pio, pin::ROW_2); - pio_gpio_init(pio, pin::ROW_3); - pio_gpio_init(pio, pin::ROW_4); - pio_gpio_init(pio, pin::ROW_5); - pio_gpio_init(pio, pin::ROW_6); - - pio_sm_set_consecutive_pindirs(pio, sm, pin::LED_DATA, 4, true); - pio_sm_set_consecutive_pindirs(pio, sm, pin::ROW_6, 7, true); - - pio_sm_config c = unicorn_program_get_default_config(offset); - - // osr shifts right, autopull on, autopull threshold 8 - sm_config_set_out_shift(&c, true, false, 32); - - // configure out, set, and sideset pins - sm_config_set_out_pins(&c, pin::ROW_6, 7); - sm_config_set_sideset_pins(&c, pin::LED_CLOCK); - sm_config_set_set_pins(&c, pin::LED_DATA, 4); - - // join fifos as only tx needed (gives 8 deep fifo instead of 4) - sm_config_set_fifo_join(&c, PIO_FIFO_JOIN_TX); - - pio_sm_init(pio, sm, offset, &c); - pio_sm_set_enabled(pio, sm, true); -} +static uint32_t dma_ctrl_channel; namespace pimoroni { + PicoUnicorn* PicoUnicorn::unicorn = nullptr; + PIO PicoUnicorn::bitstream_pio = pio0; + uint PicoUnicorn::bitstream_sm = 0; + uint PicoUnicorn::bitstream_sm_offset = 0; - // once the dma transfer of the scanline is complete we move to the - // next scanline (or quit if we're finished) - void __isr dma_complete() { - if (dma_hw->ints0 & (1u << dma_channel)) { - dma_hw->ints0 = (1u << dma_channel); // clear irq flag - dma_channel_set_trans_count(dma_channel, BITSTREAM_LENGTH / 4, false); - dma_channel_set_read_addr(dma_channel, bitstream, true); + PicoUnicorn::~PicoUnicorn() { + if(unicorn == this) { + partial_teardown(); + + dma_channel_unclaim(dma_ctrl_channel); // This works now the teardown behaves correctly + dma_channel_unclaim(dma_channel); // This works now the teardown behaves correctly + pio_sm_unclaim(bitstream_pio, bitstream_sm); + pio_remove_program(bitstream_pio, &unicorn_program, bitstream_sm_offset); + + unicorn = nullptr; } } - PicoUnicorn::~PicoUnicorn() { - // stop and release the dma channel - irq_set_enabled(DMA_IRQ_0, false); - dma_channel_set_irq0_enabled(dma_channel, false); - irq_set_enabled(pio_get_dreq(bitstream_pio, bitstream_sm, true), false); - irq_remove_handler(DMA_IRQ_0, dma_complete); + void PicoUnicorn::partial_teardown() { + // Stop the bitstream SM + pio_sm_set_enabled(bitstream_pio, bitstream_sm, false); - dma_channel_wait_for_finish_blocking(dma_channel); - dma_channel_unclaim(dma_channel); + // Make sure the display is off and switch it to an invisible row, to be safe + const uint pins_to_set = 0b1111111 << ROW_6; + pio_sm_set_pins_with_mask(bitstream_pio, bitstream_sm, pins_to_set, pins_to_set); - // release the pio and sm - pio_sm_unclaim(bitstream_pio, bitstream_sm); - pio_clear_instruction_memory(bitstream_pio); - pio_sm_restart(bitstream_pio, bitstream_sm); + dma_hw->ch[dma_ctrl_channel].al1_ctrl = (dma_hw->ch[dma_ctrl_channel].al1_ctrl & ~DMA_CH0_CTRL_TRIG_CHAIN_TO_BITS) | (dma_ctrl_channel << DMA_CH0_CTRL_TRIG_CHAIN_TO_LSB); + dma_hw->ch[dma_channel].al1_ctrl = (dma_hw->ch[dma_channel].al1_ctrl & ~DMA_CH0_CTRL_TRIG_CHAIN_TO_BITS) | (dma_channel << DMA_CH0_CTRL_TRIG_CHAIN_TO_LSB); + // Abort any in-progress DMA transfer + dma_safe_abort(dma_ctrl_channel); + dma_safe_abort(dma_channel); } + [[deprecated("Handled by constructor.")]] void PicoUnicorn::init() { - // todo: shouldn't need to do this if things were cleaned up properly but without - // this any attempt to run a micropython script twice will fail - static bool already_init = false; + return; + } + PicoUnicorn::PicoUnicorn() { + if(unicorn != nullptr) { + partial_teardown(); + } + // setup pins gpio_init(pin::LED_DATA); gpio_set_dir(pin::LED_DATA, GPIO_OUT); gpio_init(pin::LED_CLOCK); gpio_set_dir(pin::LED_CLOCK, GPIO_OUT); @@ -171,47 +142,81 @@ namespace pimoroni { gpio_set_function(pin::X, GPIO_FUNC_SIO); gpio_set_dir(pin::X, GPIO_IN); gpio_pull_up(pin::X); gpio_set_function(pin::Y, GPIO_FUNC_SIO); gpio_set_dir(pin::Y, GPIO_IN); gpio_pull_up(pin::Y); - if(already_init) { - // stop and release the dma channel - irq_set_enabled(DMA_IRQ_0, false); - dma_channel_abort(dma_channel); - dma_channel_wait_for_finish_blocking(dma_channel); - - dma_channel_set_irq0_enabled(dma_channel, false); - irq_set_enabled(pio_get_dreq(bitstream_pio, bitstream_sm, true), false); - irq_remove_handler(DMA_IRQ_0, dma_complete); - - dma_channel_unclaim(dma_channel); - - // release the pio and sm - pio_sm_unclaim(bitstream_pio, bitstream_sm); - pio_clear_instruction_memory(bitstream_pio); - pio_sm_restart(bitstream_pio, bitstream_sm); - //return; - } - // setup the pio bitstream_pio = pio0; - bitstream_sm = pio_claim_unused_sm(pio0, true); - sm_offset = pio_add_program(bitstream_pio, &unicorn_program); - unicorn_jetpack_program_init(bitstream_pio, bitstream_sm, sm_offset); + if(unicorn == nullptr) { + bitstream_sm = pio_claim_unused_sm(bitstream_pio, true); + bitstream_sm_offset = pio_add_program(bitstream_pio, &unicorn_program); + } + + pio_gpio_init(bitstream_pio, pin::LED_DATA); + pio_gpio_init(bitstream_pio, pin::LED_CLOCK); + pio_gpio_init(bitstream_pio, pin::LED_LATCH); + pio_gpio_init(bitstream_pio, pin::LED_BLANK); + pio_gpio_init(bitstream_pio, pin::ROW_0); + pio_gpio_init(bitstream_pio, pin::ROW_1); + pio_gpio_init(bitstream_pio, pin::ROW_2); + pio_gpio_init(bitstream_pio, pin::ROW_3); + pio_gpio_init(bitstream_pio, pin::ROW_4); + pio_gpio_init(bitstream_pio, pin::ROW_5); + pio_gpio_init(bitstream_pio, pin::ROW_6); - // setup dma transfer for pixel data to the pio + pio_sm_set_consecutive_pindirs(bitstream_pio, bitstream_sm, pin::LED_DATA, 4, true); + pio_sm_set_consecutive_pindirs(bitstream_pio, bitstream_sm, pin::ROW_6, 7, true); + + pio_sm_config c = unicorn_program_get_default_config(bitstream_sm_offset); + + // osr shifts right, autopull on, autopull threshold 8 + sm_config_set_out_shift(&c, true, false, 32); + + // configure out, set, and sideset pins + sm_config_set_out_pins(&c, pin::ROW_6, 7); + sm_config_set_sideset_pins(&c, pin::LED_CLOCK); + sm_config_set_set_pins(&c, pin::LED_DATA, 4); + + // join fifos as only tx needed (gives 8 deep fifo instead of 4) + sm_config_set_fifo_join(&c, PIO_FIFO_JOIN_TX); + + // setup chained dma transfer for pixel data to the pio dma_channel = dma_claim_unused_channel(true); + dma_ctrl_channel = dma_claim_unused_channel(true); + + dma_channel_config ctrl_config = dma_channel_get_default_config(dma_ctrl_channel); + channel_config_set_transfer_data_size(&ctrl_config, DMA_SIZE_32); + channel_config_set_read_increment(&ctrl_config, false); + channel_config_set_write_increment(&ctrl_config, false); + channel_config_set_chain_to(&ctrl_config, dma_channel); + + dma_channel_configure( + dma_ctrl_channel, + &ctrl_config, + &dma_hw->ch[dma_channel].read_addr, + &bitstream_addr, + 1, + false + ); + dma_channel_config config = dma_channel_get_default_config(dma_channel); channel_config_set_transfer_data_size(&config, DMA_SIZE_32); channel_config_set_bswap(&config, false); // byte swap to reverse little endian channel_config_set_dreq(&config, pio_get_dreq(bitstream_pio, bitstream_sm, true)); - dma_channel_configure(dma_channel, &config, &bitstream_pio->txf[bitstream_sm], NULL, 0, false); - dma_channel_set_irq0_enabled(dma_channel, true); - irq_set_enabled(pio_get_dreq(bitstream_pio, bitstream_sm, true), true); - irq_set_exclusive_handler(DMA_IRQ_0, dma_complete); - irq_set_enabled(DMA_IRQ_0, true); + channel_config_set_chain_to(&config, dma_ctrl_channel); - dma_channel_set_trans_count(dma_channel, BITSTREAM_LENGTH / 4, false); - dma_channel_set_read_addr(dma_channel, bitstream, true); + dma_channel_configure( + dma_channel, + &config, + &bitstream_pio->txf[bitstream_sm], + NULL, + BITSTREAM_LENGTH / 4, + false); - already_init = true; + pio_sm_init(bitstream_pio, bitstream_sm, bitstream_sm_offset, &c); + pio_sm_set_enabled(bitstream_pio, bitstream_sm, true); + + // start the control channel + dma_start_channel_mask(1u << dma_ctrl_channel); + + unicorn = this; } void PicoUnicorn::clear() { @@ -269,4 +274,21 @@ namespace pimoroni { return !gpio_get(button); } + void PicoUnicorn::dma_safe_abort(uint channel) { + // Tear down the DMA channel. + // This is copied from: https://github.com/raspberrypi/pico-sdk/pull/744/commits/5e0e8004dd790f0155426e6689a66e08a83cd9fc + uint32_t irq0_save = dma_hw->inte0 & (1u << channel); + hw_clear_bits(&dma_hw->inte0, irq0_save); + + dma_hw->abort = 1u << channel; + + // To fence off on in-flight transfers, the BUSY bit should be polled + // rather than the ABORT bit, because the ABORT bit can clear prematurely. + while (dma_hw->ch[channel].ctrl_trig & DMA_CH0_CTRL_TRIG_BUSY_BITS) tight_loop_contents(); + + // Clear the interrupt (if any) and restore the interrupt masks. + dma_hw->ints0 = 1u << channel; + hw_set_bits(&dma_hw->inte0, irq0_save); + } + } diff --git a/libraries/pico_unicorn/pico_unicorn.hpp b/libraries/pico_unicorn/pico_unicorn.hpp index 9c7f9570..a251ecd1 100644 --- a/libraries/pico_unicorn/pico_unicorn.hpp +++ b/libraries/pico_unicorn/pico_unicorn.hpp @@ -12,11 +12,24 @@ namespace pimoroni { static const uint8_t B = 13; static const uint8_t X = 14; static const uint8_t Y = 15; + + static const uint32_t ROW_COUNT = 7; + static const uint32_t ROW_BYTES = 12; + static const uint32_t BCD_FRAMES = 15; // includes fet discharge frame + static const uint32_t BITSTREAM_LENGTH = (ROW_COUNT * ROW_BYTES * BCD_FRAMES); + private: - PIO bitstream_pio = pio0; - uint bitstream_sm = 0; - uint sm_offset = 0; + static PIO bitstream_pio; + static uint bitstream_sm; + static uint bitstream_sm_offset; + + // must be aligned for 32bit dma transfer + alignas(4) uint8_t bitstream[BITSTREAM_LENGTH] = {0}; + const uint32_t bitstream_addr = (uint32_t)bitstream; + static PicoUnicorn* unicorn; + public: + PicoUnicorn(); ~PicoUnicorn(); void init(); @@ -26,6 +39,9 @@ namespace pimoroni { void set_pixel(uint8_t x, uint8_t y, uint8_t v); bool is_pressed(uint8_t button); + private: + void partial_teardown(); + void dma_safe_abort(uint channel); }; } \ No newline at end of file diff --git a/micropython/examples/pico_unicorn/demo.py b/micropython/examples/pico_unicorn/demo.py index 9a3cb8ba..f488ee41 100644 --- a/micropython/examples/pico_unicorn/demo.py +++ b/micropython/examples/pico_unicorn/demo.py @@ -1,6 +1,6 @@ -import picounicorn +from picounicorn import PicoUnicorn -picounicorn.init() +picounicorn = PicoUnicorn() # From CPython Lib/colorsys.py diff --git a/micropython/examples/pico_unicorn/rainbow.py b/micropython/examples/pico_unicorn/rainbow.py index 40b2d26f..073a2b28 100644 --- a/micropython/examples/pico_unicorn/rainbow.py +++ b/micropython/examples/pico_unicorn/rainbow.py @@ -1,7 +1,7 @@ -import picounicorn +from picounicorn import PicoUnicorn import time -picounicorn.init() +picounicorn = PicoUnicorn() # From CPython Lib/colorsys.py diff --git a/micropython/modules/pico_unicorn/pico_unicorn.c b/micropython/modules/pico_unicorn/pico_unicorn.c index 8fcc54ad..37b80c29 100755 --- a/micropython/modules/pico_unicorn/pico_unicorn.c +++ b/micropython/modules/pico_unicorn/pico_unicorn.c @@ -16,30 +16,53 @@ enum buttons //////////////////////////////////////////////////////////////////////////////////////////////////// /***** Module Functions *****/ -STATIC MP_DEFINE_CONST_FUN_OBJ_0(picounicorn_init_obj, picounicorn_init); -STATIC MP_DEFINE_CONST_FUN_OBJ_0(picounicorn_get_width_obj, picounicorn_get_width); -STATIC MP_DEFINE_CONST_FUN_OBJ_0(picounicorn_get_height_obj, picounicorn_get_height); -//STATIC MP_DEFINE_CONST_FUN_OBJ_0(picounicorn_update_obj, picounicorn_update); -STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(picounicorn_set_pixel_obj, 5, 5, picounicorn_set_pixel); -STATIC MP_DEFINE_CONST_FUN_OBJ_3(picounicorn_set_pixel_value_obj, picounicorn_set_pixel_value); -STATIC MP_DEFINE_CONST_FUN_OBJ_0(picounicorn_clear_obj, picounicorn_clear); -STATIC MP_DEFINE_CONST_FUN_OBJ_1(picounicorn_is_pressed_obj, picounicorn_is_pressed); +STATIC MP_DEFINE_CONST_FUN_OBJ_1(picounicorn__del__obj, picounicorn__del__); +STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(picounicorn_set_pixel_obj, 6, 6, picounicorn_set_pixel); +STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(picounicorn_set_pixel_value_obj, 4, 4, picounicorn_set_pixel_value); +STATIC MP_DEFINE_CONST_FUN_OBJ_1(picounicorn_clear_obj, picounicorn_clear); +STATIC MP_DEFINE_CONST_FUN_OBJ_2(picounicorn_is_pressed_obj, picounicorn_is_pressed); +STATIC MP_DEFINE_CONST_FUN_OBJ_1(picounicorn_get_width_obj, picounicorn_get_width); +STATIC MP_DEFINE_CONST_FUN_OBJ_1(picounicorn_get_height_obj, picounicorn_get_height); -/***** Globals Table *****/ -STATIC const mp_rom_map_elem_t picounicorn_globals_table[] = { - { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_picounicorn) }, - { MP_ROM_QSTR(MP_QSTR_init), MP_ROM_PTR(&picounicorn_init_obj) }, - { MP_ROM_QSTR(MP_QSTR_get_width), MP_ROM_PTR(&picounicorn_get_width_obj) }, - { MP_ROM_QSTR(MP_QSTR_get_height), MP_ROM_PTR(&picounicorn_get_height_obj) }, - //{ MP_ROM_QSTR(MP_QSTR_update), MP_ROM_PTR(&picounicorn_update_obj) }, +STATIC const mp_rom_map_elem_t picounicorn_locals_dict_table[] = { + { MP_ROM_QSTR(MP_QSTR___del__), MP_ROM_PTR(&picounicorn__del__obj) }, { MP_ROM_QSTR(MP_QSTR_set_pixel), MP_ROM_PTR(&picounicorn_set_pixel_obj) }, { MP_ROM_QSTR(MP_QSTR_set_pixel_value), MP_ROM_PTR(&picounicorn_set_pixel_value_obj) }, { MP_ROM_QSTR(MP_QSTR_clear), MP_ROM_PTR(&picounicorn_clear_obj) }, - { MP_ROM_QSTR(MP_QSTR_is_pressed), MP_ROM_PTR(&picounicorn_is_pressed_obj) }, + { MP_ROM_QSTR(MP_QSTR_is_pressed), MP_ROM_PTR(&picounicorn_is_pressed_obj) }, + { MP_ROM_QSTR(MP_QSTR_get_width), MP_ROM_PTR(&picounicorn_get_width_obj) }, + { MP_ROM_QSTR(MP_QSTR_get_height), MP_ROM_PTR(&picounicorn_get_height_obj) }, +}; + +STATIC MP_DEFINE_CONST_DICT(picounicorn_locals_dict, picounicorn_locals_dict_table); + +#ifdef MP_DEFINE_CONST_OBJ_TYPE +MP_DEFINE_CONST_OBJ_TYPE( + picounicorn_type, + MP_QSTR_PicoUnicorn, + MP_TYPE_FLAG_NONE, + make_new, picounicorn_make_new, + locals_dict, (mp_obj_dict_t*)&picounicorn_locals_dict +); +#else +const mp_obj_type_t picounicorn_type = { + { &mp_type_type }, + .name = MP_QSTR_PicoUnicorn, + .make_new = picounicorn_make_new, + .locals_dict = (mp_obj_dict_t*)&picounicorn_locals_dict, +}; +#endif + +/***** Globals Table *****/ +STATIC const mp_rom_map_elem_t picounicorn_globals_table[] = { + { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_picounicorn) }, + { MP_OBJ_NEW_QSTR(MP_QSTR_PicoUnicorn), (mp_obj_t)&picounicorn_type }, { MP_ROM_QSTR(MP_QSTR_BUTTON_A), MP_ROM_INT(BUTTON_A) }, { MP_ROM_QSTR(MP_QSTR_BUTTON_B), MP_ROM_INT(BUTTON_B) }, { MP_ROM_QSTR(MP_QSTR_BUTTON_X), MP_ROM_INT(BUTTON_X) }, { MP_ROM_QSTR(MP_QSTR_BUTTON_Y), MP_ROM_INT(BUTTON_Y) }, + { MP_ROM_QSTR(MP_QSTR_WIDTH), MP_ROM_INT(16) }, + { MP_ROM_QSTR(MP_QSTR_HEIGHT), MP_ROM_INT(7) }, }; STATIC MP_DEFINE_CONST_DICT(mp_module_picounicorn_globals, picounicorn_globals_table); diff --git a/micropython/modules/pico_unicorn/pico_unicorn.cpp b/micropython/modules/pico_unicorn/pico_unicorn.cpp index 86f4e90f..eab23eff 100644 --- a/micropython/modules/pico_unicorn/pico_unicorn.cpp +++ b/micropython/modules/pico_unicorn/pico_unicorn.cpp @@ -7,122 +7,116 @@ using namespace pimoroni; -PicoUnicorn *unicorn = nullptr; - extern "C" { #include "pico_unicorn.h" -#define NOT_INITIALISED_MSG "Cannot call this function, as picounicorn is not initialised. Call picounicorn.init() first." +typedef struct _picounicorn_obj_t { + mp_obj_base_t base; + PicoUnicorn *unicorn; +} picounicorn_obj_t; -mp_obj_t picounicorn_init() { - if(unicorn == nullptr) - unicorn = m_tracked_alloc_class(PicoUnicorn); - unicorn->init(); +mp_obj_t picounicorn_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) { + picounicorn_obj_t *self = m_new_obj_with_finaliser(picounicorn_obj_t); + self->base.type = &picounicorn_type; + self->unicorn = m_new_class(PicoUnicorn); + return MP_OBJ_FROM_PTR(self); +} + +mp_obj_t picounicorn__del__(mp_obj_t self_in) { + picounicorn_obj_t *self = MP_OBJ_TO_PTR2(self_in, picounicorn_obj_t); + m_del_class(PicoUnicorn, self->unicorn); return mp_const_none; } -mp_obj_t picounicorn_get_width() { +mp_obj_t picounicorn_get_width(mp_obj_t self_in) { + (void)self_in; return mp_obj_new_int(PicoUnicorn::WIDTH); } -mp_obj_t picounicorn_get_height() { +mp_obj_t picounicorn_get_height(mp_obj_t self_in) { + (void)self_in; return mp_obj_new_int(PicoUnicorn::HEIGHT); } -// mp_obj_t picounicorn_update() { -// unicorn.update(); -// return mp_const_none; -// } - mp_obj_t picounicorn_set_pixel(mp_uint_t n_args, const mp_obj_t *args) { - (void)n_args; //Unused input parameter, we know it's 5 + (void)n_args; // Unused input parameter, we know it's 5 - if(unicorn != nullptr) { - int x = mp_obj_get_int(args[0]); - int y = mp_obj_get_int(args[1]); - int r = mp_obj_get_int(args[2]); - int g = mp_obj_get_int(args[3]); - int b = mp_obj_get_int(args[4]); + enum { ARG_self, ARG_x, ARG_y, ARG_r, ARG_g, ARG_b }; - if(x < 0 || x >= PicoUnicorn::WIDTH || y < 0 || y >= PicoUnicorn::HEIGHT) - mp_raise_ValueError("x or y out of range."); - else - { - if(r < 0 || r > 255) - mp_raise_ValueError("r out of range. Expected 0 to 255"); - else if(g < 0 || g > 255) - mp_raise_ValueError("g out of range. Expected 0 to 255"); - else if(b < 0 || b > 255) - mp_raise_ValueError("b out of range. Expected 0 to 255"); - else - unicorn->set_pixel(x, y, r, g, b); - } + picounicorn_obj_t *self = MP_OBJ_TO_PTR2(args[ARG_self], picounicorn_obj_t); + + int x = mp_obj_get_int(args[ARG_x]); + int y = mp_obj_get_int(args[ARG_y]); + int r = mp_obj_get_int(args[ARG_r]); + int g = mp_obj_get_int(args[ARG_g]); + int b = mp_obj_get_int(args[ARG_b]); + + if(x < 0 || x >= PicoUnicorn::WIDTH || y < 0 || y >= PicoUnicorn::HEIGHT) { + mp_raise_ValueError("x or y out of range."); } - else - mp_raise_msg(&mp_type_RuntimeError, NOT_INITIALISED_MSG); + + if(r < 0 || r > 255) mp_raise_ValueError("r out of range. Expected 0 to 255"); + if(g < 0 || g > 255) mp_raise_ValueError("g out of range. Expected 0 to 255"); + if(b < 0 || b > 255) mp_raise_ValueError("b out of range. Expected 0 to 255"); + self->unicorn->set_pixel(x, y, r, g, b); return mp_const_none; } -mp_obj_t picounicorn_set_pixel_value(mp_obj_t x_obj, mp_obj_t y_obj, mp_obj_t v_obj) { - if(unicorn != nullptr) { - int x = mp_obj_get_int(x_obj); - int y = mp_obj_get_int(y_obj); - int val = mp_obj_get_int(v_obj); +mp_obj_t picounicorn_set_pixel_value(size_t n_args, const mp_obj_t *args) { + enum { ARG_self, ARG_x, ARG_y, ARG_v }; - if(x < 0 || x >= PicoUnicorn::WIDTH || y < 0 || y >= PicoUnicorn::HEIGHT) - mp_raise_ValueError("x or y out of range."); - else { - if(val < 0 || val > 255) - mp_raise_ValueError("val out of range. Expected 0 to 255"); - else - unicorn->set_pixel(x, y, val); - } + picounicorn_obj_t *self = MP_OBJ_TO_PTR2(args[ARG_self], picounicorn_obj_t); + + int x = mp_obj_get_int(args[ARG_x]); + int y = mp_obj_get_int(args[ARG_y]); + int val = mp_obj_get_int(args[ARG_v]); + + if(x < 0 || x >= PicoUnicorn::WIDTH || y < 0 || y >= PicoUnicorn::HEIGHT) { + mp_raise_ValueError("x or y out of range."); } - else - mp_raise_msg(&mp_type_RuntimeError, NOT_INITIALISED_MSG); + + if(val < 0 || val > 255) mp_raise_ValueError("val out of range. Expected 0 to 255"); + + self->unicorn->set_pixel(x, y, val); return mp_const_none; } -mp_obj_t picounicorn_clear() { - if(unicorn != nullptr) - unicorn->clear(); - else - mp_raise_msg(&mp_type_RuntimeError, NOT_INITIALISED_MSG); +mp_obj_t picounicorn_clear(mp_obj_t self_in) { + picounicorn_obj_t *self = MP_OBJ_TO_PTR2(self_in, picounicorn_obj_t); + self->unicorn->clear(); return mp_const_none; } -mp_obj_t picounicorn_is_pressed(mp_obj_t button_obj) { +mp_obj_t picounicorn_is_pressed(mp_obj_t self_in, mp_obj_t button_obj) { + picounicorn_obj_t *self = MP_OBJ_TO_PTR2(self_in, picounicorn_obj_t); + bool buttonPressed = false; - - if(unicorn != nullptr) { - int buttonID = mp_obj_get_int(button_obj); - switch(buttonID) { - case 0: - buttonPressed = unicorn->is_pressed(PicoUnicorn::A); - break; + int buttonID = mp_obj_get_int(button_obj); - case 1: - buttonPressed = unicorn->is_pressed(PicoUnicorn::B); - break; + switch(buttonID) { + case 0: + buttonPressed = self->unicorn->is_pressed(PicoUnicorn::A); + break; - case 2: - buttonPressed = unicorn->is_pressed(PicoUnicorn::X); - break; + case 1: + buttonPressed = self->unicorn->is_pressed(PicoUnicorn::B); + break; - case 3: - buttonPressed = unicorn->is_pressed(PicoUnicorn::Y); - break; + case 2: + buttonPressed = self->unicorn->is_pressed(PicoUnicorn::X); + break; - default: - mp_raise_ValueError("button not valid. Expected 0 to 3"); - break; - } + case 3: + buttonPressed = self->unicorn->is_pressed(PicoUnicorn::Y); + break; + + default: + mp_raise_ValueError("button not valid. Expected 0 to 3"); + break; } - else - mp_raise_msg(&mp_type_RuntimeError, NOT_INITIALISED_MSG); return buttonPressed ? mp_const_true : mp_const_false; } diff --git a/micropython/modules/pico_unicorn/pico_unicorn.h b/micropython/modules/pico_unicorn/pico_unicorn.h index cf3d43cc..0ffa5f91 100644 --- a/micropython/modules/pico_unicorn/pico_unicorn.h +++ b/micropython/modules/pico_unicorn/pico_unicorn.h @@ -1,13 +1,13 @@ -// Include MicroPython API. -//#include "py/obj.h" #include "py/runtime.h" -// Declare the functions we'll make available in Python -extern mp_obj_t picounicorn_init(); -extern mp_obj_t picounicorn_get_width(); -extern mp_obj_t picounicorn_get_height(); -//extern mp_obj_t picounicorn_update(); +extern const mp_obj_type_t picounicorn_type; + +extern mp_obj_t picounicorn_get_width(mp_obj_t self_in); +extern mp_obj_t picounicorn_get_height(mp_obj_t self_in); + +extern mp_obj_t picounicorn__del__(mp_obj_t self_in); +extern mp_obj_t picounicorn_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args); extern mp_obj_t picounicorn_set_pixel(mp_uint_t n_args, const mp_obj_t *args); -extern mp_obj_t picounicorn_set_pixel_value(mp_obj_t x_obj, mp_obj_t y_obj, mp_obj_t v_obj); -extern mp_obj_t picounicorn_clear(); -extern mp_obj_t picounicorn_is_pressed(mp_obj_t button_obj); \ No newline at end of file +extern mp_obj_t picounicorn_set_pixel_value(mp_uint_t n_args, const mp_obj_t *args); +extern mp_obj_t picounicorn_clear(mp_obj_t self_in); +extern mp_obj_t picounicorn_is_pressed(mp_obj_t self_in, mp_obj_t button_obj); \ No newline at end of file