diff --git a/drivers/dv_display/dv_display.cpp b/drivers/dv_display/dv_display.cpp index 2d8a07f8..64d05ea2 100644 --- a/drivers/dv_display/dv_display.cpp +++ b/drivers/dv_display/dv_display.cpp @@ -398,16 +398,75 @@ namespace pimoroni { buf[2] = full_width << 16; buf[3] = (uint32_t)display_height << 16; buf[4] = 0x00000001; - buf[5] = 0x00010000 + display_height + (bank << 24); - buf[6] = 0x00000001; + buf[5] = 0x00010000 + display_height + ((uint32_t)bank << 24); + buf[6] = 0x04000001; ram.write(0, buf, 7 * 4); ram.wait_for_finish_blocking(); } + void DVDisplay::write_sprite_table() + { + constexpr uint32_t buf_size = 32; + uint32_t buf[buf_size]; + + uint addr = (display_height + 7) * 4 + PALETTE_SIZE * 3; + uint sprite_type = 0x10000000u; + for (uint32_t i = 0; i < max_num_sprites; i += buf_size) { + for (uint32_t j = 0; j < buf_size; ++j) { + buf[j] = sprite_type + (i + j) * sprite_size + sprite_base_address; + } + ram.write(addr, buf, buf_size * 4); + ram.wait_for_finish_blocking(); + addr += buf_size * 4; + } + } + void DVDisplay::write_header() { write_header_preamble(); set_scroll_idx_for_lines(1, 0, display_height); + write_sprite_table(); + } + + void DVDisplay::define_sprite(uint16_t sprite_data_idx, uint16_t width, uint16_t height, uint16_t* data) + { + uint32_t buf[33]; + uint16_t* buf_ptr = (uint16_t*)buf; + uint addr = sprite_base_address + sprite_data_idx * sprite_size; + + *buf_ptr++ = (height << 8) + width; + + for (uint16_t i = 0; i < height; ++i) + { + *buf_ptr++ = width << 8; + } + uint len = (uint8_t*)buf_ptr - (uint8_t*)buf; + ram.write(addr, buf, len); + + addr += len; + if (len & 2) addr += 2; + + ram.write(addr, (uint32_t*)data, width * height * 2); + } + + void DVDisplay::set_sprite(int sprite_num, uint16_t sprite_data_idx, const Point &p) + { + uint8_t buf[7]; + buf[0] = 1; // BLEND_DEPTH + buf[1] = sprite_data_idx & 0xff; + buf[2] = sprite_data_idx >> 8; + buf[3] = p.x & 0xff; + buf[4] = p.x >> 8; + buf[5] = p.y & 0xff; + buf[6] = p.y >> 8; + + i2c->write_bytes(I2C_ADDR, sprite_num, buf, 7); + } + + void DVDisplay::clear_sprite(int sprite_num) + { + uint16_t off = 0xFFFF; + i2c->write_bytes(I2C_ADDR, sprite_num, (uint8_t*)&off, 2); } uint32_t DVDisplay::point_to_address(const Point& p) const { diff --git a/drivers/dv_display/dv_display.hpp b/drivers/dv_display/dv_display.hpp index 059f41b3..796c982f 100644 --- a/drivers/dv_display/dv_display.hpp +++ b/drivers/dv_display/dv_display.hpp @@ -60,6 +60,9 @@ namespace pimoroni { static constexpr uint RAM_SEL = 8; static constexpr uint32_t base_address = 0x10000; + static constexpr uint32_t max_num_sprites = 1024; + static constexpr uint32_t sprite_size = 0x900; + static constexpr uint32_t sprite_base_address = 0x800000 - (max_num_sprites * sprite_size); uint16_t display_width = 0; uint16_t display_height = 0; uint16_t frame_width = 0; @@ -134,6 +137,20 @@ namespace pimoroni { void flip(); void reset(); + // Define the data for a sprite, the specified data index can then be supplied + // to set_sprite to use the sprite. Up to 1024 sprites can be defined. + // Each sprite can be up to 2KB big, with a maximum width or height of 64 pixels. + // So for ARGB1555 sprites 64x16, 32x32, 16x64 are examples of maximum sizes. + // Sprites are currently only supported when using ARGB1555 mode. + // You must define the sprite to each RAM bank. + void define_sprite(uint16_t sprite_data_idx, uint16_t width, uint16_t height, uint16_t* data); + + // Display/move a sprite to a given position. + // Note sprite positions are always display relative (not scrolled) + // TODO: Blend mode + void set_sprite(int sprite_num, uint16_t sprite_data_idx, const Point &p); + void clear_sprite(int sprite_num); + // 32 colour palette mode. Note that palette entries range from 0-31, // but when writing colour values the palette entry is in bits 6-2, so the // entry value is effectively multiplied by 4. @@ -183,8 +200,9 @@ namespace pimoroni { virtual void write_palette(); virtual void write_header(); + virtual void write_sprite_table(); + virtual void write_header_preamble(); - void write_header_preamble(); void i2c_modify_bit(uint8_t reg, uint bit, bool enable); private: diff --git a/examples/dv_stick/dv_sprite_test.cmake b/examples/dv_stick/dv_sprite_test.cmake new file mode 100644 index 00000000..a7935b23 --- /dev/null +++ b/examples/dv_stick/dv_sprite_test.cmake @@ -0,0 +1,15 @@ +set(OUTPUT_NAME dv_sprite_test) + +add_executable( + ${OUTPUT_NAME} + dv_sprite_test.cpp + edid-decode.c +) + +# Pull in pico libraries that we need +target_link_libraries(${OUTPUT_NAME} pico_stdlib dv_display hardware_i2c hardware_uart pico_graphics) + +pico_enable_stdio_usb(${OUTPUT_NAME} 1) + +# create map/bin/hex file etc. +pico_add_extra_outputs(${OUTPUT_NAME}) diff --git a/examples/dv_stick/dv_sprite_test.cpp b/examples/dv_stick/dv_sprite_test.cpp new file mode 100644 index 00000000..223e447c --- /dev/null +++ b/examples/dv_stick/dv_sprite_test.cpp @@ -0,0 +1,97 @@ +#include +#include + +#include +#include "hardware/gpio.h" +#include "hardware/uart.h" +#include "drivers/dv_display/dv_display.hpp" +#include "libraries/pico_graphics/pico_graphics.hpp" + +using namespace pimoroni; + +#define DISPLAY_WIDTH 360 +#define DISPLAY_HEIGHT 240 + +#define FRAME_WIDTH 720 +#define FRAME_HEIGHT 480 + +#define SPRITE_WIDTH 8 +#define SPRITE_HEIGHT 8 + +static uint16_t sprite_data[] = { + 0x0001, 0x0001, 0x0001, 0xF000, 0xF000, 0x0001, 0x0001, 0x0001, + 0x0001, 0x0001, 0xF000, 0xF000, 0xF000, 0xF000, 0x0001, 0x0001, + 0x0001, 0xF000, 0xF000, 0xF000, 0xF000, 0xF000, 0xF000, 0x0001, + 0xF000, 0xF000, 0xF000, 0xF000, 0xF000, 0xF000, 0xF000, 0xF000, + 0xF000, 0xF000, 0xF000, 0xF000, 0xF000, 0xF000, 0xF000, 0xF000, + 0x0001, 0xF000, 0xF000, 0xF000, 0xF000, 0xF000, 0xF000, 0x0001, + 0x0001, 0x0001, 0xF000, 0xF000, 0xF000, 0xF000, 0x0001, 0x0001, + 0x0001, 0x0001, 0x0001, 0xF000, 0xF000, 0x0001, 0x0001, 0x0001, +}; + +void on_uart_rx() { + while (uart_is_readable(uart1)) { + uint8_t ch = uart_getc(uart1); + putc(ch, stdout); + } +} + +int main() { + stdio_init_all(); + + // Relay UART RX from the display driver + gpio_set_function(5, GPIO_FUNC_UART); + uart_init(uart1, 115200); + uart_set_hw_flow(uart1, false, false); + uart_set_format(uart1, 8, 1, UART_PARITY_NONE); + uart_set_fifo_enabled(uart1, false); + irq_set_exclusive_handler(UART1_IRQ, on_uart_rx); + irq_set_enabled(UART1_IRQ, true); + uart_set_irq_enables(uart1, true, false); + + constexpr uint BUTTON_A = 9; + gpio_init(BUTTON_A); + gpio_set_dir(BUTTON_A, GPIO_IN); + gpio_pull_up(BUTTON_A); + + //sleep_ms(5000); + + DVDisplay display; + display.init(DISPLAY_WIDTH, DISPLAY_HEIGHT, DVDisplay::MODE_RGB555, FRAME_WIDTH, FRAME_HEIGHT); + PicoGraphics_PenDV_RGB555 graphics(FRAME_WIDTH, FRAME_HEIGHT, display); + + display.define_sprite(0, SPRITE_WIDTH, SPRITE_HEIGHT, sprite_data); + graphics.set_pen(0x000F); + graphics.clear(); + display.flip(); + + constexpr int NUM_SPRITES = 32; + Point sprite_pos[NUM_SPRITES]; + Point sprite_dir[NUM_SPRITES]; + + for (int i = 0; i < 32; ++i) { + sprite_pos[i].x = rand() % DISPLAY_WIDTH; + sprite_pos[i].y = rand() % DISPLAY_HEIGHT; + sprite_dir[i].x = (rand() & 1) ? 1 : -1; + sprite_dir[i].y = (rand() & 1) ? 1 : -1; + } + + while (true) + { + for (int i = 0; i < 32; ++i) { + display.set_sprite(i, 0, sprite_pos[i]); + + sprite_pos[i] += sprite_dir[i]; + if (sprite_pos[i].x >= DISPLAY_WIDTH || sprite_pos[i].x <= 0) { + sprite_dir[i].x = -sprite_dir[i].x; + sprite_pos[i].x += sprite_dir[i].x; + } + if (sprite_pos[i].y >= DISPLAY_HEIGHT || sprite_pos[i].y <= 0) { + sprite_dir[i].y = -sprite_dir[i].y; + sprite_pos[i].y += sprite_dir[i].y; + } + } + + sleep_ms(16); + } +} diff --git a/examples/dv_stick/dv_stick_test.cpp b/examples/dv_stick/dv_stick_test.cpp index e8361f31..bb730874 100644 --- a/examples/dv_stick/dv_stick_test.cpp +++ b/examples/dv_stick/dv_stick_test.cpp @@ -49,7 +49,7 @@ int main() { gpio_set_dir(BUTTON_A, GPIO_IN); gpio_pull_up(BUTTON_A); - sleep_ms(5000); + //sleep_ms(5000); DVDisplay display; display.init(DISPLAY_WIDTH, DISPLAY_HEIGHT, DVDisplay::MODE_RGB888, FRAME_WIDTH, FRAME_HEIGHT);