Pico Unicorn: Refactor into class.

Borrow heavily from Galactic/Cosmic Unicorn for the PIO/chained-DMA setup.
This commit is contained in:
Phil Howard 2022-07-25 11:36:15 +01:00 committed by Phil Howard
parent 044313551b
commit 3eb42336e6
9 changed files with 264 additions and 222 deletions

View File

@ -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;

View File

@ -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++

View File

@ -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);
}
}

View File

@ -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);
};
}

View File

@ -1,6 +1,6 @@
import picounicorn
from picounicorn import PicoUnicorn
picounicorn.init()
picounicorn = PicoUnicorn()
# From CPython Lib/colorsys.py

View File

@ -1,7 +1,7 @@
import picounicorn
from picounicorn import PicoUnicorn
import time
picounicorn.init()
picounicorn = PicoUnicorn()
# From CPython Lib/colorsys.py

View File

@ -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);

View File

@ -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;
}

View File

@ -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);