Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor MicroPython hacks out of Pico Unicorn #463

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
92 changes: 43 additions & 49 deletions libraries/pico_unicorn/pico_unicorn.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -44,16 +44,6 @@ 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);
Expand Down Expand Up @@ -87,39 +77,55 @@ static inline void unicorn_jetpack_program_init(PIO pio, uint sm, uint offset) {
pio_sm_set_enabled(pio, sm, true);
}

static pimoroni::PicoUnicorn *unicorn = nullptr;

void __isr picounicorn_dma_complete() {
if(unicorn) {
unicorn->dma_complete();
}
}

namespace pimoroni {

// 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
void PicoUnicorn::dma_complete() {
if(dma_channel_get_irq0_status(dma_channel)) {
dma_channel_acknowledge_irq0(dma_channel);

dma_channel_set_trans_count(dma_channel, BITSTREAM_LENGTH / 4, false);
dma_channel_set_read_addr(dma_channel, bitstream, true);
dma_channel_set_read_addr(dma_channel, buffer, true);
}
}

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

dma_channel_wait_for_finish_blocking(dma_channel);
dma_channel_unclaim(dma_channel);
if(dma_channel_is_claimed(dma_channel)) {
dma_channel_set_irq0_enabled(dma_channel, false);
irq_remove_handler(DMA_IRQ_0, picounicorn_dma_complete);
//dma_channel_wait_for_finish_blocking(dma_channel);
dma_channel_abort(dma_channel);
dma_channel_acknowledge_irq0(dma_channel);
dma_channel_unclaim(dma_channel);
}

if(pio_sm_is_claimed(bitstream_pio, bitstream_sm)) {
pio_sm_set_enabled(bitstream_pio, bitstream_sm, false);
pio_sm_drain_tx_fifo(bitstream_pio, bitstream_sm);
pio_sm_unclaim(bitstream_pio, bitstream_sm);
}

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

[[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() {
// 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);
Expand All @@ -134,6 +140,8 @@ namespace pimoroni {
gpio_init(pin::ROW_5); gpio_set_dir(pin::ROW_5, GPIO_OUT);
gpio_init(pin::ROW_6); gpio_set_dir(pin::ROW_6, GPIO_OUT);

uint8_t *bitstream = (uint8_t *)buffer;

// initialise the bcd timing values and row selects in the bitstream
for(uint8_t row = 0; row < HEIGHT; row++) {
for(uint8_t frame = 0; frame < BCD_FRAMES; frame++) {
Expand Down Expand Up @@ -171,25 +179,6 @@ 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);
Expand All @@ -203,15 +192,18 @@ namespace pimoroni {
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_exclusive_handler(DMA_IRQ_0, picounicorn_dma_complete);
irq_set_enabled(pio_get_dreq(bitstream_pio, bitstream_sm, true), true);
irq_set_exclusive_handler(DMA_IRQ_0, dma_complete);

dma_channel_set_irq0_enabled(dma_channel, true);

irq_set_enabled(DMA_IRQ_0, true);

dma_channel_set_trans_count(dma_channel, BITSTREAM_LENGTH / 4, false);
dma_channel_set_read_addr(dma_channel, bitstream, true);
unicorn = this;

already_init = true;
dma_channel_set_trans_count(dma_channel, BITSTREAM_LENGTH / 4, false);
dma_channel_set_read_addr(dma_channel, buffer, true);
}

void PicoUnicorn::clear() {
Expand All @@ -225,6 +217,8 @@ namespace pimoroni {
void PicoUnicorn::set_pixel(uint8_t x, uint8_t y, uint8_t r, uint8_t g, uint8_t b) {
if(x < 0 || x >= WIDTH || y < 0 || y >= HEIGHT) return;

uint8_t *bitstream = (uint8_t *)buffer;

// make those coordinates sane
x = (WIDTH - 1) - x;

Expand Down
15 changes: 14 additions & 1 deletion libraries/pico_unicorn/pico_unicorn.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,21 +4,34 @@

namespace pimoroni {

class PicoUnicorn {
class alignas(4) PicoUnicorn {
public:
static const int WIDTH = 16;
static const int HEIGHT = 7;
static const uint8_t A = 12;
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;
uint dma_channel;
// must be aligned for 32bit dma transfer
alignas(4) uint32_t buffer[BITSTREAM_LENGTH / 4] = {0};

public:
PicoUnicorn();
~PicoUnicorn();

void dma_complete();

void init();

void clear();
Expand Down
48 changes: 34 additions & 14 deletions micropython/modules/pico_unicorn/pico_unicorn.c
Original file line number Diff line number Diff line change
Expand Up @@ -16,26 +16,46 @@ enum buttons
////////////////////////////////////////////////////////////////////////////////////////////////////

/***** Module Functions *****/
STATIC MP_DEFINE_CONST_FUN_OBJ_0(picounicorn_init_obj, picounicorn_init);
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__del__obj, picounicorn__del__);

STATIC const mp_rom_map_elem_t picounicorn_locals_dict[] = {
{ 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___del__), MP_ROM_PTR(&picounicorn__del__obj) },
};

#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

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

/***** 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___name__), MP_ROM_QSTR(MP_QSTR_picounicorn) },
{ MP_OBJ_NEW_QSTR(MP_QSTR_PicoUnicorn), (mp_obj_t)&picounicorn_type },
{ 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_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_get_height), MP_ROM_PTR(&picounicorn_get_height_obj) },
{ 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) },
Expand Down
Loading