kopia lustrzana https://github.com/pimoroni/pimoroni-pico
Pico Unicorn: Refactor into class.
Borrow heavily from Galactic/Cosmic Unicorn for the PIO/chained-DMA setup.patch-jpegdec-filename
rodzic
044313551b
commit
3eb42336e6
|
@ -10,9 +10,6 @@ using namespace pimoroni;
|
||||||
PicoUnicorn pico_unicorn;
|
PicoUnicorn pico_unicorn;
|
||||||
|
|
||||||
int main() {
|
int main() {
|
||||||
|
|
||||||
pico_unicorn.init();
|
|
||||||
|
|
||||||
bool a_pressed = false;
|
bool a_pressed = false;
|
||||||
bool b_pressed = false;
|
bool b_pressed = false;
|
||||||
bool x_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)
|
- [Buttons](#buttons)
|
||||||
- [WIDTH / HEIGHT](#width--height)
|
- [WIDTH / HEIGHT](#width--height)
|
||||||
- [Functions](#functions)
|
- [Functions](#functions)
|
||||||
- [init](#init)
|
- [set\_pixel](#set_pixel)
|
||||||
- [set_pixel](#set_pixel)
|
- [is\_pressed](#is_pressed)
|
||||||
- [is_pressed](#is_pressed)
|
|
||||||
|
|
||||||
## Example Program
|
## 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++
|
```c++
|
||||||
|
|
||||||
|
@ -48,15 +47,6 @@ int num_pixels = pico_unicorn.WIDTH * pico_unicorn.HEIGHT;
|
||||||
|
|
||||||
### Functions
|
### 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
|
#### set_pixel
|
||||||
|
|
||||||
```c++
|
```c++
|
||||||
|
|
|
@ -44,82 +44,53 @@ enum pin {
|
||||||
Y = 15,
|
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 uint32_t dma_channel;
|
||||||
|
static uint32_t dma_ctrl_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);
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace pimoroni {
|
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
|
PicoUnicorn::~PicoUnicorn() {
|
||||||
// next scanline (or quit if we're finished)
|
if(unicorn == this) {
|
||||||
void __isr dma_complete() {
|
partial_teardown();
|
||||||
if (dma_hw->ints0 & (1u << dma_channel)) {
|
|
||||||
dma_hw->ints0 = (1u << dma_channel); // clear irq flag
|
dma_channel_unclaim(dma_ctrl_channel); // This works now the teardown behaves correctly
|
||||||
dma_channel_set_trans_count(dma_channel, BITSTREAM_LENGTH / 4, false);
|
dma_channel_unclaim(dma_channel); // This works now the teardown behaves correctly
|
||||||
dma_channel_set_read_addr(dma_channel, bitstream, true);
|
pio_sm_unclaim(bitstream_pio, bitstream_sm);
|
||||||
|
pio_remove_program(bitstream_pio, &unicorn_program, bitstream_sm_offset);
|
||||||
|
|
||||||
|
unicorn = nullptr;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
PicoUnicorn::~PicoUnicorn() {
|
void PicoUnicorn::partial_teardown() {
|
||||||
// stop and release the dma channel
|
// Stop the bitstream SM
|
||||||
irq_set_enabled(DMA_IRQ_0, false);
|
pio_sm_set_enabled(bitstream_pio, bitstream_sm, 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);
|
|
||||||
|
|
||||||
dma_channel_wait_for_finish_blocking(dma_channel);
|
// Make sure the display is off and switch it to an invisible row, to be safe
|
||||||
dma_channel_unclaim(dma_channel);
|
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
|
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);
|
||||||
pio_sm_unclaim(bitstream_pio, bitstream_sm);
|
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);
|
||||||
pio_clear_instruction_memory(bitstream_pio);
|
// Abort any in-progress DMA transfer
|
||||||
pio_sm_restart(bitstream_pio, bitstream_sm);
|
dma_safe_abort(dma_ctrl_channel);
|
||||||
|
dma_safe_abort(dma_channel);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[[deprecated("Handled by constructor.")]]
|
||||||
void PicoUnicorn::init() {
|
void PicoUnicorn::init() {
|
||||||
// todo: shouldn't need to do this if things were cleaned up properly but without
|
return;
|
||||||
// this any attempt to run a micropython script twice will fail
|
}
|
||||||
static bool already_init = false;
|
|
||||||
|
|
||||||
|
PicoUnicorn::PicoUnicorn() {
|
||||||
|
if(unicorn != nullptr) {
|
||||||
|
partial_teardown();
|
||||||
|
}
|
||||||
|
|
||||||
// setup pins
|
// setup pins
|
||||||
gpio_init(pin::LED_DATA); gpio_set_dir(pin::LED_DATA, GPIO_OUT);
|
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);
|
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::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);
|
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
|
// setup the pio
|
||||||
bitstream_pio = pio0;
|
bitstream_pio = pio0;
|
||||||
bitstream_sm = pio_claim_unused_sm(pio0, true);
|
if(unicorn == nullptr) {
|
||||||
sm_offset = pio_add_program(bitstream_pio, &unicorn_program);
|
bitstream_sm = pio_claim_unused_sm(bitstream_pio, true);
|
||||||
unicorn_jetpack_program_init(bitstream_pio, bitstream_sm, sm_offset);
|
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_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);
|
dma_channel_config config = dma_channel_get_default_config(dma_channel);
|
||||||
channel_config_set_transfer_data_size(&config, DMA_SIZE_32);
|
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_bswap(&config, false); // byte swap to reverse little endian
|
||||||
channel_config_set_dreq(&config, pio_get_dreq(bitstream_pio, bitstream_sm, true));
|
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);
|
channel_config_set_chain_to(&config, dma_ctrl_channel);
|
||||||
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);
|
|
||||||
|
|
||||||
dma_channel_set_trans_count(dma_channel, BITSTREAM_LENGTH / 4, false);
|
dma_channel_configure(
|
||||||
dma_channel_set_read_addr(dma_channel, bitstream, true);
|
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() {
|
void PicoUnicorn::clear() {
|
||||||
|
@ -269,4 +274,21 @@ namespace pimoroni {
|
||||||
return !gpio_get(button);
|
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 B = 13;
|
||||||
static const uint8_t X = 14;
|
static const uint8_t X = 14;
|
||||||
static const uint8_t Y = 15;
|
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:
|
private:
|
||||||
PIO bitstream_pio = pio0;
|
static PIO bitstream_pio;
|
||||||
uint bitstream_sm = 0;
|
static uint bitstream_sm;
|
||||||
uint sm_offset = 0;
|
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:
|
public:
|
||||||
|
PicoUnicorn();
|
||||||
~PicoUnicorn();
|
~PicoUnicorn();
|
||||||
|
|
||||||
void init();
|
void init();
|
||||||
|
@ -26,6 +39,9 @@ namespace pimoroni {
|
||||||
void set_pixel(uint8_t x, uint8_t y, uint8_t v);
|
void set_pixel(uint8_t x, uint8_t y, uint8_t v);
|
||||||
|
|
||||||
bool is_pressed(uint8_t button);
|
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
|
# From CPython Lib/colorsys.py
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import picounicorn
|
from picounicorn import PicoUnicorn
|
||||||
import time
|
import time
|
||||||
|
|
||||||
picounicorn.init()
|
picounicorn = PicoUnicorn()
|
||||||
|
|
||||||
|
|
||||||
# From CPython Lib/colorsys.py
|
# From CPython Lib/colorsys.py
|
||||||
|
|
|
@ -16,30 +16,53 @@ enum buttons
|
||||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
/***** Module Functions *****/
|
/***** Module Functions *****/
|
||||||
STATIC MP_DEFINE_CONST_FUN_OBJ_0(picounicorn_init_obj, picounicorn_init);
|
STATIC MP_DEFINE_CONST_FUN_OBJ_1(picounicorn__del__obj, picounicorn__del__);
|
||||||
STATIC MP_DEFINE_CONST_FUN_OBJ_0(picounicorn_get_width_obj, picounicorn_get_width);
|
STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(picounicorn_set_pixel_obj, 6, 6, picounicorn_set_pixel);
|
||||||
STATIC MP_DEFINE_CONST_FUN_OBJ_0(picounicorn_get_height_obj, picounicorn_get_height);
|
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_0(picounicorn_update_obj, picounicorn_update);
|
STATIC MP_DEFINE_CONST_FUN_OBJ_1(picounicorn_clear_obj, picounicorn_clear);
|
||||||
STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(picounicorn_set_pixel_obj, 5, 5, picounicorn_set_pixel);
|
STATIC MP_DEFINE_CONST_FUN_OBJ_2(picounicorn_is_pressed_obj, picounicorn_is_pressed);
|
||||||
STATIC MP_DEFINE_CONST_FUN_OBJ_3(picounicorn_set_pixel_value_obj, picounicorn_set_pixel_value);
|
STATIC MP_DEFINE_CONST_FUN_OBJ_1(picounicorn_get_width_obj, picounicorn_get_width);
|
||||||
STATIC MP_DEFINE_CONST_FUN_OBJ_0(picounicorn_clear_obj, picounicorn_clear);
|
STATIC MP_DEFINE_CONST_FUN_OBJ_1(picounicorn_get_height_obj, picounicorn_get_height);
|
||||||
STATIC MP_DEFINE_CONST_FUN_OBJ_1(picounicorn_is_pressed_obj, picounicorn_is_pressed);
|
|
||||||
|
|
||||||
/***** Globals Table *****/
|
STATIC const mp_rom_map_elem_t picounicorn_locals_dict_table[] = {
|
||||||
STATIC const mp_rom_map_elem_t picounicorn_globals_table[] = {
|
{ MP_ROM_QSTR(MP_QSTR___del__), MP_ROM_PTR(&picounicorn__del__obj) },
|
||||||
{ 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) },
|
|
||||||
{ MP_ROM_QSTR(MP_QSTR_set_pixel), MP_ROM_PTR(&picounicorn_set_pixel_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_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_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_A), MP_ROM_INT(BUTTON_A) },
|
||||||
{ MP_ROM_QSTR(MP_QSTR_BUTTON_B), MP_ROM_INT(BUTTON_B) },
|
{ 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_X), MP_ROM_INT(BUTTON_X) },
|
||||||
{ MP_ROM_QSTR(MP_QSTR_BUTTON_Y), MP_ROM_INT(BUTTON_Y) },
|
{ 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);
|
STATIC MP_DEFINE_CONST_DICT(mp_module_picounicorn_globals, picounicorn_globals_table);
|
||||||
|
|
||||||
|
|
|
@ -7,122 +7,116 @@
|
||||||
|
|
||||||
using namespace pimoroni;
|
using namespace pimoroni;
|
||||||
|
|
||||||
PicoUnicorn *unicorn = nullptr;
|
|
||||||
|
|
||||||
|
|
||||||
extern "C" {
|
extern "C" {
|
||||||
#include "pico_unicorn.h"
|
#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() {
|
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) {
|
||||||
if(unicorn == nullptr)
|
picounicorn_obj_t *self = m_new_obj_with_finaliser(picounicorn_obj_t);
|
||||||
unicorn = m_tracked_alloc_class(PicoUnicorn);
|
self->base.type = &picounicorn_type;
|
||||||
unicorn->init();
|
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;
|
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);
|
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);
|
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) {
|
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) {
|
enum { ARG_self, ARG_x, ARG_y, ARG_r, ARG_g, ARG_b };
|
||||||
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]);
|
|
||||||
|
|
||||||
if(x < 0 || x >= PicoUnicorn::WIDTH || y < 0 || y >= PicoUnicorn::HEIGHT)
|
picounicorn_obj_t *self = MP_OBJ_TO_PTR2(args[ARG_self], picounicorn_obj_t);
|
||||||
mp_raise_ValueError("x or y out of range.");
|
|
||||||
else
|
int x = mp_obj_get_int(args[ARG_x]);
|
||||||
{
|
int y = mp_obj_get_int(args[ARG_y]);
|
||||||
if(r < 0 || r > 255)
|
int r = mp_obj_get_int(args[ARG_r]);
|
||||||
mp_raise_ValueError("r out of range. Expected 0 to 255");
|
int g = mp_obj_get_int(args[ARG_g]);
|
||||||
else if(g < 0 || g > 255)
|
int b = mp_obj_get_int(args[ARG_b]);
|
||||||
mp_raise_ValueError("g out of range. Expected 0 to 255");
|
|
||||||
else if(b < 0 || b > 255)
|
if(x < 0 || x >= PicoUnicorn::WIDTH || y < 0 || y >= PicoUnicorn::HEIGHT) {
|
||||||
mp_raise_ValueError("b out of range. Expected 0 to 255");
|
mp_raise_ValueError("x or y out of range.");
|
||||||
else
|
|
||||||
unicorn->set_pixel(x, y, r, g, b);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
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;
|
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) {
|
mp_obj_t picounicorn_set_pixel_value(size_t n_args, const mp_obj_t *args) {
|
||||||
if(unicorn != nullptr) {
|
enum { ARG_self, ARG_x, ARG_y, ARG_v };
|
||||||
int x = mp_obj_get_int(x_obj);
|
|
||||||
int y = mp_obj_get_int(y_obj);
|
|
||||||
int val = mp_obj_get_int(v_obj);
|
|
||||||
|
|
||||||
if(x < 0 || x >= PicoUnicorn::WIDTH || y < 0 || y >= PicoUnicorn::HEIGHT)
|
picounicorn_obj_t *self = MP_OBJ_TO_PTR2(args[ARG_self], picounicorn_obj_t);
|
||||||
mp_raise_ValueError("x or y out of range.");
|
|
||||||
else {
|
int x = mp_obj_get_int(args[ARG_x]);
|
||||||
if(val < 0 || val > 255)
|
int y = mp_obj_get_int(args[ARG_y]);
|
||||||
mp_raise_ValueError("val out of range. Expected 0 to 255");
|
int val = mp_obj_get_int(args[ARG_v]);
|
||||||
else
|
|
||||||
unicorn->set_pixel(x, y, val);
|
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;
|
return mp_const_none;
|
||||||
}
|
}
|
||||||
|
|
||||||
mp_obj_t picounicorn_clear() {
|
mp_obj_t picounicorn_clear(mp_obj_t self_in) {
|
||||||
if(unicorn != nullptr)
|
picounicorn_obj_t *self = MP_OBJ_TO_PTR2(self_in, picounicorn_obj_t);
|
||||||
unicorn->clear();
|
self->unicorn->clear();
|
||||||
else
|
|
||||||
mp_raise_msg(&mp_type_RuntimeError, NOT_INITIALISED_MSG);
|
|
||||||
return mp_const_none;
|
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;
|
bool buttonPressed = false;
|
||||||
|
int buttonID = mp_obj_get_int(button_obj);
|
||||||
if(unicorn != nullptr) {
|
|
||||||
int buttonID = mp_obj_get_int(button_obj);
|
|
||||||
switch(buttonID) {
|
|
||||||
case 0:
|
|
||||||
buttonPressed = unicorn->is_pressed(PicoUnicorn::A);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 1:
|
switch(buttonID) {
|
||||||
buttonPressed = unicorn->is_pressed(PicoUnicorn::B);
|
case 0:
|
||||||
break;
|
buttonPressed = self->unicorn->is_pressed(PicoUnicorn::A);
|
||||||
|
break;
|
||||||
|
|
||||||
case 2:
|
case 1:
|
||||||
buttonPressed = unicorn->is_pressed(PicoUnicorn::X);
|
buttonPressed = self->unicorn->is_pressed(PicoUnicorn::B);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 3:
|
case 2:
|
||||||
buttonPressed = unicorn->is_pressed(PicoUnicorn::Y);
|
buttonPressed = self->unicorn->is_pressed(PicoUnicorn::X);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
case 3:
|
||||||
mp_raise_ValueError("button not valid. Expected 0 to 3");
|
buttonPressed = self->unicorn->is_pressed(PicoUnicorn::Y);
|
||||||
break;
|
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;
|
return buttonPressed ? mp_const_true : mp_const_false;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,13 +1,13 @@
|
||||||
// Include MicroPython API.
|
|
||||||
//#include "py/obj.h"
|
|
||||||
#include "py/runtime.h"
|
#include "py/runtime.h"
|
||||||
|
|
||||||
// Declare the functions we'll make available in Python
|
extern const mp_obj_type_t picounicorn_type;
|
||||||
extern mp_obj_t picounicorn_init();
|
|
||||||
extern mp_obj_t picounicorn_get_width();
|
extern mp_obj_t picounicorn_get_width(mp_obj_t self_in);
|
||||||
extern mp_obj_t picounicorn_get_height();
|
extern mp_obj_t picounicorn_get_height(mp_obj_t self_in);
|
||||||
//extern mp_obj_t picounicorn_update();
|
|
||||||
|
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(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_set_pixel_value(mp_uint_t n_args, const mp_obj_t *args);
|
||||||
extern mp_obj_t picounicorn_clear();
|
extern mp_obj_t picounicorn_clear(mp_obj_t self_in);
|
||||||
extern mp_obj_t picounicorn_is_pressed(mp_obj_t button_obj);
|
extern mp_obj_t picounicorn_is_pressed(mp_obj_t self_in, mp_obj_t button_obj);
|
Ładowanie…
Reference in New Issue