Pico Unicorn: Refactor into class.
Borrow heavily from Galactic/Cosmic Unicorn for the PIO/chained-DMA setup.
This commit is contained in:
parent
044313551b
commit
3eb42336e6
|
@ -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;
|
||||
|
|
|
@ -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++
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
};
|
||||
|
||||
}
|
|
@ -1,6 +1,6 @@
|
|||
import picounicorn
|
||||
from picounicorn import PicoUnicorn
|
||||
|
||||
picounicorn.init()
|
||||
picounicorn = PicoUnicorn()
|
||||
|
||||
|
||||
# From CPython Lib/colorsys.py
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import picounicorn
|
||||
from picounicorn import PicoUnicorn
|
||||
import time
|
||||
|
||||
picounicorn.init()
|
||||
picounicorn = PicoUnicorn()
|
||||
|
||||
|
||||
# From CPython Lib/colorsys.py
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
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);
|
Loading…
Reference in New Issue