From dad3d1466439ce9927ac8e8ab1745f98642b9380 Mon Sep 17 00:00:00 2001 From: Phillip Burgess Date: Tue, 17 Jan 2023 14:22:18 -0800 Subject: [PATCH] Quick port of the spitft2hdmi code into Arduino --- examples/virtual_spitft/virtual_spitft.ino | 262 ++++++++++++++++++++- src/fourwire.pio | 24 ++ 2 files changed, 285 insertions(+), 1 deletion(-) create mode 100644 src/fourwire.pio diff --git a/examples/virtual_spitft/virtual_spitft.ino b/examples/virtual_spitft/virtual_spitft.ino index 4b287a8..09edcc0 100644 --- a/examples/virtual_spitft/virtual_spitft.ino +++ b/examples/virtual_spitft/virtual_spitft.ino @@ -2,7 +2,81 @@ // over 4-wire SPI interface, mimicking functionality of displays such as // ILI9341, but shown on monitor instead. -// THIS IS A PLACEHOLDER AND NOT ACTUALLY WRITTEN YET. +// Quick-n-dirty port without regard to screen size and stuff, and +// some pins are hardcoded and stuff. Just proof-of-concept stuff, +// getting the Pico SDK version of this code adapted to Arduino and +// the PicoDVI library. + +// Output of pioasm ---- + +#define fourwire_wrap_target 2 +#define fourwire_wrap 5 + +static const uint16_t fourwire_program_instructions[] = { + 0xa0c3, // 0: mov isr, null + 0x0005, // 1: jmp 5 + // .wrap_target + 0x2022, // 2: wait 0 pin, 2 + 0x20a2, // 3: wait 1 pin, 2 + 0x4002, // 4: in pins, 2 + 0x00c0, // 5: jmp pin, 0 + // .wrap +}; + +static const struct pio_program fourwire_program = { + .instructions = fourwire_program_instructions, + .length = 6, + .origin = -1, +}; + +static inline pio_sm_config fourwire_program_get_default_config(uint offset) { + pio_sm_config c = pio_get_default_sm_config(); + sm_config_set_wrap(&c, offset + fourwire_wrap_target, offset + fourwire_wrap); + return c; +} + +// end pioasm output ---- + +PIO pio = pio1; + +#define BIT_DEPOSIT(b, i) ((b) ? (1<<(i)) : 0) +#define BIT_EXTRACT(b, i) (((b) >> (i)) & 1) +#define BIT_MOVE(b, src, dest) BIT_DEPOSIT(BIT_EXTRACT(b, src), dest) +#define ENCODED_COMMAND(x) ( \ + (BIT_MOVE(x, 0, 0)) | \ + (BIT_MOVE(x, 1, 2)) | \ + (BIT_MOVE(x, 2, 4)) | \ + (BIT_MOVE(x, 3, 6)) | \ + (BIT_MOVE(x, 4, 8)) | \ + (BIT_MOVE(x, 5, 10)) | \ + (BIT_MOVE(x, 6, 12)) | \ + (BIT_MOVE(x, 7, 14)) \ +) + +uint8_t decode[256]; + +struct { + uint8_t x0h, x0l, x1h, x1l, y0h, y0l, y1h, y1l; +} bounds; + +#define COMMAND_CASET (0x2a) +#define COMMAND_PASET (0x2b) +#define COMMAND_RAMWR (0x2c) + +static int clamped(int x, int lo, int hi) { + if(x < lo) { return lo; } + if(x > hi) { return hi; } + return x; +} + +#define GET_WORD() (word = pio_sm_get_blocking(pio, sm)) +#define GET_DATA() do { GET_WORD(); if(IS_COMMAND()) goto next_command; } while(0) +#define IS_COMMAND() (!(word & 0x2)) +#define DECODED() (decode[(word & 0x55) | ((word >> 7) & 0xaa)]) + +uint16_t *framebuf; +uint sm; + #include // Core display & graphics library @@ -18,7 +92,193 @@ void setup() { pinMode(LED_BUILTIN, OUTPUT); for (;;) digitalWrite(LED_BUILTIN, (millis() / 500) & 1); } + + framebuf = display.getBuffer(); + + for(int i=0; i<256; i++) { + int j = (BIT_MOVE(i, 0, 0)) | + (BIT_MOVE(i, 2, 1)) | + (BIT_MOVE(i, 4, 2)) | + (BIT_MOVE(i, 6, 3)) | + (BIT_MOVE(i, 1, 4)) | + (BIT_MOVE(i, 3, 5)) | + (BIT_MOVE(i, 5, 6)) | + (BIT_MOVE(i, 7, 7)); + decode[i] = j; + } + + uint offset = pio_add_program(pio, &fourwire_program); + sm = pio_claim_unused_sm(pio, true); + + uint pin = 18; + uint jmp_pin = 21; + + pio_sm_config c = fourwire_program_get_default_config(offset); + + // Set the IN base pin to the provided `pin` parameter. This is the data + // pin, and the next-numbered GPIO is used as the clock pin. + sm_config_set_in_pins(&c, pin); + sm_config_set_jmp_pin(&c, jmp_pin); + // Set the pin directions to input at the PIO + pio_sm_set_consecutive_pindirs(pio, sm, pin, 3, false); + pio_sm_set_consecutive_pindirs(pio, sm, 1, 1, false); + pio_sm_set_consecutive_pindirs(pio, sm, jmp_pin, 1, false); + // Connect these GPIOs to this PIO block + pio_gpio_init(pio, pin); + pio_gpio_init(pio, pin + 1); + pio_gpio_init(pio, pin + 2); + pio_gpio_init(pio, jmp_pin); + // set pulls + gpio_set_pulls(pin, true, false); + gpio_set_pulls(pin + 1, true, false); + gpio_set_pulls(pin + 2, true, false); + gpio_set_pulls(jmp_pin, true, false); + + // Shifting to left matches the customary MSB-first ordering of SPI. + sm_config_set_in_shift( + &c, + false, // Shift-to-right = false (i.e. shift to left) + true, // Autopush enabled + 16 // Autopush threshold + ); + + // We only receive, so disable the TX FIFO to make the RX FIFO deeper. + sm_config_set_fifo_join(&c, PIO_FIFO_JOIN_RX); + + // Load our configuration, and start the program from the beginning + pio_sm_init(pio, sm, offset, &c); + pio_sm_set_enabled(pio, sm, true); } void loop() { +uint16_t word = 0; + + int x=0, y=0; + while(true) { + GET_WORD(); +next_command: + switch(word) { + case ENCODED_COMMAND(COMMAND_CASET): + GET_DATA(); + bounds.x0h = DECODED(); + + GET_DATA(); + bounds.x0l = DECODED(); + + GET_DATA(); + bounds.x1h = DECODED(); + + GET_DATA(); + bounds.x1l = DECODED(); + break; + + case ENCODED_COMMAND(COMMAND_PASET): + GET_DATA(); + bounds.y0h = DECODED(); + + GET_DATA(); + bounds.y0l = DECODED(); + + GET_DATA(); + bounds.y1h = DECODED(); + + GET_DATA(); + bounds.y1l = DECODED(); + break; + + case ENCODED_COMMAND(COMMAND_RAMWR): + { + int X0 = bounds.x0l | (bounds.x0h << 8); + X0 = clamped(X0, 0, display.width()); + int X1 = bounds.x1l | (bounds.x1h << 8); + X1 = clamped(X1, X0, display.width()) + 1; + + int Y0 = bounds.y0l | (bounds.y0h << 8); + Y0 = clamped(Y0, 0, display.height()); + int Y1 = bounds.y1l | (bounds.y1h << 8); + Y1 = clamped(Y1, Y0, display.height()) + 1; + while(1) { + for(y=Y0; y