perform updates via repeated dma transfer instead of blocking writes to the pio fifo

pull/1/head
Jonathan Williamson 2021-01-19 07:09:58 +00:00
rodzic 944355f060
commit ded4087db4
4 zmienionych plików z 50 dodań i 22 usunięć

Wyświetl plik

@ -128,8 +128,8 @@ int main() {
}
}
sleep_ms(10);
pico_unicorn.update();
}
printf("done\n");

Wyświetl plik

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

Wyświetl plik

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

Wyświetl plik

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