Skip to content

Commit

Permalink
CHANGE ME
Browse files Browse the repository at this point in the history
  • Loading branch information
Gadgetoid committed Jul 25, 2022
1 parent c5d4865 commit 62a6c8e
Show file tree
Hide file tree
Showing 5 changed files with 166 additions and 160 deletions.
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
38 changes: 24 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,36 @@ 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) },
};

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

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

0 comments on commit 62a6c8e

Please sign in to comment.