From ded4087db418791466149ae1c6beb1e54f3e3bae Mon Sep 17 00:00:00 2001 From: Jonathan Williamson Date: Tue, 19 Jan 2021 07:09:58 +0000 Subject: [PATCH] perform updates via repeated dma transfer instead of blocking writes to the pio fifo --- examples/pico_unicorn/demo.cpp | 2 +- libraries/pico_unicorn/pico_unicorn.cpp | 53 ++++++++++++++++++------- libraries/pico_unicorn/pico_unicorn.hpp | 3 +- libraries/pico_unicorn/pico_unicorn.pio | 14 ++++--- 4 files changed, 50 insertions(+), 22 deletions(-) diff --git a/examples/pico_unicorn/demo.cpp b/examples/pico_unicorn/demo.cpp index 94a59967..51d253e6 100644 --- a/examples/pico_unicorn/demo.cpp +++ b/examples/pico_unicorn/demo.cpp @@ -128,8 +128,8 @@ int main() { } } + sleep_ms(10); - pico_unicorn.update(); } printf("done\n"); diff --git a/libraries/pico_unicorn/pico_unicorn.cpp b/libraries/pico_unicorn/pico_unicorn.cpp index 82b9e392..ac078d60 100644 --- a/libraries/pico_unicorn/pico_unicorn.cpp +++ b/libraries/pico_unicorn/pico_unicorn.cpp @@ -16,8 +16,10 @@ // each row looks like this: // // 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, # pixel data +// 0b00000000, # dummy byte to 32-bit align the frame (could be used to extend row select in future) // 0b01111111, # row 0 select (7-bit row address, 1-bit dummy data) // 0b00001111, 0b11111111, # bcd tick count (0-65536) +// // .. next BCD frame for this row (repeat for 14 frames) // // .. next row (repeat for 7 rows) @@ -75,7 +77,7 @@ static inline void unicorn_jetpack_program_init(PIO pio, uint sm, uint offset) { 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, true, 32); + 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); @@ -94,6 +96,21 @@ static inline void unicorn_jetpack_program_init(PIO pio, uint sm, uint offset) { 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 + dma_channel_set_trans_count(dma_channel, BITSTREAM_LENGTH / 4, false); + dma_channel_set_read_addr(dma_channel, bitstream, true); + + static bool hilo = false; + gpio_put(2, hilo); + hilo = !hilo; + } + } + + void PicoUnicorn::init() { // setup pins gpio_init(pin::LED_DATA); gpio_set_dir(pin::LED_DATA, GPIO_OUT); @@ -101,6 +118,8 @@ namespace pimoroni { gpio_init(pin::LED_LATCH); gpio_set_dir(pin::LED_LATCH, GPIO_OUT); gpio_init(pin::LED_BLANK); gpio_set_dir(pin::LED_BLANK, GPIO_OUT); + gpio_init(2); gpio_set_dir(2, GPIO_OUT); + gpio_init(pin::ROW_0); gpio_set_dir(pin::ROW_0, GPIO_OUT); gpio_init(pin::ROW_1); gpio_set_dir(pin::ROW_1, GPIO_OUT); gpio_init(pin::ROW_2); gpio_set_dir(pin::ROW_2, GPIO_OUT); @@ -129,13 +148,13 @@ namespace pimoroni { // determine offset in the buffer for this row/frame uint16_t offset = (row * ROW_BYTES * BCD_FRAMES) + (ROW_BYTES * frame); - uint16_t row_select_offset = offset + 8; + uint16_t row_select_offset = offset + 9; + uint16_t bcd_offset = offset + 10; // the last bcd frame is used to allow the fets to discharge to avoid ghosting if(frame == BCD_FRAMES - 1) { bitstream[row_select_offset] = 0b01111111; - uint16_t bcd_offset = offset + 9; uint16_t bcd_ticks = 65535; bitstream[bcd_offset + 1] = (bcd_ticks & 0xff00) >> 8; bitstream[bcd_offset] = (bcd_ticks & 0xff); @@ -143,7 +162,6 @@ namespace pimoroni { uint8_t row_select_mask = ~(1 << (7 - row)); bitstream[row_select_offset] = row_select_mask; - uint16_t bcd_offset = offset + 9; uint16_t bcd_ticks = pow(2, frame); bitstream[bcd_offset + 1] = (bcd_ticks & 0xff00) >> 8; bitstream[bcd_offset] = (bcd_ticks & 0xff); @@ -153,15 +171,30 @@ namespace pimoroni { // setup the pio bitstream_pio = pio0; - sm = 0; + bitstream_sm = 0; uint offset = pio_add_program(bitstream_pio, &unicorn_program); - unicorn_jetpack_program_init(bitstream_pio, sm, offset); + unicorn_jetpack_program_init(bitstream_pio, bitstream_sm, offset); // setup button inputs gpio_set_function(pin::A, GPIO_FUNC_SIO); gpio_set_dir(pin::A, GPIO_IN); gpio_pull_up(pin::A); gpio_set_function(pin::B, GPIO_FUNC_SIO); gpio_set_dir(pin::B, GPIO_IN); gpio_pull_up(pin::B); 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); + + // setup dma transfer for pixel data to the pio + dma_channel = dma_claim_unused_channel(true); + 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); + + dma_channel_set_trans_count(dma_channel, BITSTREAM_LENGTH / 4, false); + dma_channel_set_read_addr(dma_channel, bitstream, true); } void PicoUnicorn::clear() { @@ -215,14 +248,6 @@ namespace pimoroni { set_pixel(x, y, v, v, v); } - void PicoUnicorn::update() { - // copy data to pio tx fifo 4 bytes at a time - uint32_t *p = (uint32_t *)bitstream; - for(uint16_t i = 0; i < BITSTREAM_LENGTH; i+=4) { - pio_sm_put_blocking(bitstream_pio, sm, *p++); - } - } - bool PicoUnicorn::is_pressed(uint8_t button) { return !gpio_get(button); } diff --git a/libraries/pico_unicorn/pico_unicorn.hpp b/libraries/pico_unicorn/pico_unicorn.hpp index 5fe75379..49f5b089 100644 --- a/libraries/pico_unicorn/pico_unicorn.hpp +++ b/libraries/pico_unicorn/pico_unicorn.hpp @@ -7,11 +7,10 @@ namespace pimoroni { class PicoUnicorn { uint32_t __fb[16 * 7]; PIO bitstream_pio = pio0; - uint sm = 0; + uint bitstream_sm = 0; public: void init(); - void update(); void clear(); void set_pixel(uint8_t x, uint8_t y, uint8_t r, uint8_t g, uint8_t b); diff --git a/libraries/pico_unicorn/pico_unicorn.pio b/libraries/pico_unicorn/pico_unicorn.pio index 53bf3c02..466a0bc0 100644 --- a/libraries/pico_unicorn/pico_unicorn.pio +++ b/libraries/pico_unicorn/pico_unicorn.pio @@ -25,6 +25,8 @@ pixels: + pull ifempty + ; dummy bit used to align pixel data to nibbles out null, 1 ; discard @@ -54,6 +56,11 @@ endb: ; jmp y-- pixels ; jump back to start of pixel loop + pull + +; dummy byte to 32 bit align row data + out null, 8 + ; select active row out null, 1 ; discard dummy bit out pins, 7 ; output row selection mask @@ -70,14 +77,11 @@ endb: ; bcd_count: jmp x-- bcd_count ; loop until bcd delay complete -; disable led output (blank) and clear latch pin - set pins, 8 - ; disable all row outputs set x, 0 ; load x register with 0 (we can't set more than 5 bits at a time) mov pins, !x ; write inverted x (0xff) to row pins latching them all high -; dummy byte to 32 bit align row data - out null, 8 +; disable led output (blank) and clear latch pin + set pins, 8 .wrap \ No newline at end of file