From 1a54f7b77dd8d3b891a31f9fce79ec09f2e7ce46 Mon Sep 17 00:00:00 2001 From: Mike Bell Date: Thu, 29 Jun 2023 20:39:42 +0100 Subject: [PATCH] DV Display: Ability to specify a larger frame than the display, and scroll it --- drivers/dv_display/dv_display.cpp | 76 ++++++++++++++++++++--------- drivers/dv_display/dv_display.hpp | 34 ++++++++----- examples/dv_stick/dv_stick_test.cpp | 19 ++++++-- 3 files changed, 93 insertions(+), 36 deletions(-) diff --git a/drivers/dv_display/dv_display.cpp b/drivers/dv_display/dv_display.cpp index dd7a3bfa..c608ffaf 100644 --- a/drivers/dv_display/dv_display.cpp +++ b/drivers/dv_display/dv_display.cpp @@ -15,24 +15,30 @@ extern "C" { namespace pimoroni { - void DVDisplay::init(uint16_t width, uint16_t height, Mode mode) { - this->width = width; - this->height = height; - this->mode = mode; + void DVDisplay::init(uint16_t display_width_, uint16_t display_height_, Mode mode_, uint16_t frame_width_, uint16_t frame_height_) { + display_width = display_width_; + display_height = display_height_; + mode = mode_; + + if (frame_width == 0) frame_width = display_width_; + else frame_width = frame_width_; + + if (frame_height == 0) frame_height = display_height_; + else frame_height = frame_height_; bank = 0; h_repeat = 1; v_repeat = 1; uint8_t res_mode = 0xFF; - uint16_t full_width = width; - uint16_t full_height = height; + uint16_t full_width = display_width; + uint16_t full_height = display_height; - if (width < 640 || (width == 640 && (height == 360 || height == 720))) { + if (display_width < 640 || (display_width == 640 && (display_height == 360 || display_height == 720))) { h_repeat = 2; full_width *= 2; } - if (height < 400) { + if (display_height < 400) { v_repeat = 2; full_height *= 2; } @@ -58,7 +64,7 @@ namespace pimoroni { } if (res_mode == 0xFF) { - mp_printf(&mp_plat_print, "Resolution %dx%d is not supported. Will use 720x480.\n", width, height); + mp_printf(&mp_plat_print, "Resolution %dx%d is not supported. Will use 720x480.\n", display_width, display_height); } gpio_init(RAM_SEL); @@ -105,7 +111,7 @@ namespace pimoroni { } } else if (pixel_buffer_location.y != -1) { - ram.write(point_to_address(pixel_buffer_location), pixel_buffer, pixel_buffer_x << 1); + ram.write(point_to_address16(pixel_buffer_location), pixel_buffer, pixel_buffer_x << 1); pixel_buffer_location.y = -1; } bank ^= 1; @@ -122,6 +128,14 @@ namespace pimoroni { swd_reset(); } + void DVDisplay::set_display_offset(const Point& p) { + int32_t offset = (int32_t)point_to_address(p) - (int32_t)point_to_address({0,0}); + while (offset & 3) { + offset -= pixel_size(); + } + i2c->write_bytes(I2C_ADDR, I2C_REG_SCROLL, (uint8_t*)&offset, 4); + } + uint8_t DVDisplay::get_gpio() { return i2c->reg_read_uint8(I2C_ADDR, I2C_REG_GPIO); } @@ -232,13 +246,13 @@ namespace pimoroni { if (pixel_buffer_x & 1) pixel_buffer[pixel_buffer_x >> 1] |= (uint32_t)colour << 16; else pixel_buffer[pixel_buffer_x >> 1] = colour; if (++pixel_buffer_x == PIXEL_BUFFER_LEN_IN_WORDS * 2) { - ram.write(point_to_address(pixel_buffer_location), pixel_buffer, PIXEL_BUFFER_LEN_IN_WORDS * 4); + ram.write(point_to_address16(pixel_buffer_location), pixel_buffer, PIXEL_BUFFER_LEN_IN_WORDS * 4); pixel_buffer_location.y = -1; } return; } else if (pixel_buffer_location.y != -1) { - ram.write(point_to_address(pixel_buffer_location), pixel_buffer, pixel_buffer_x << 1); + ram.write(point_to_address16(pixel_buffer_location), pixel_buffer, pixel_buffer_x << 1); } pixel_buffer_location = p; pixel_buffer_x = 1; @@ -247,7 +261,7 @@ namespace pimoroni { void DVDisplay::write_pixel_span(const Point &p, uint l, uint16_t colour) { - write(point_to_address(p), l, colour); + write(point_to_address16(p), l, colour); } void DVDisplay::write_pixel(const Point &p, RGB888 colour) @@ -265,18 +279,18 @@ namespace pimoroni { uint32_t offset = 0; if (((uintptr_t)data & 0x2) != 0) { uint32_t val = *data++; - ram.write(point_to_address(p), &val, 2); + ram.write(point_to_address16(p), &val, 2); --l; offset = 2; } if (l > 0) { - ram.write(point_to_address(p) + offset, (uint32_t*)data, l << 1); + ram.write(point_to_address16(p) + offset, (uint32_t*)data, l << 1); } } void DVDisplay::read_pixel_span(const Point &p, uint l, uint16_t *data) { - read(point_to_address(p), l, data); + read(point_to_address16(p), l, data); } void DVDisplay::set_mode(Mode new_mode) @@ -305,7 +319,7 @@ namespace pimoroni { void DVDisplay::write_palette() { - uint addr = (height + 7) * 4; + uint addr = (display_height + 7) * 4; ram.write(addr, (uint32_t*)palette, PALETTE_SIZE * 3); } @@ -355,13 +369,13 @@ namespace pimoroni { void DVDisplay::write_header_preamble() { uint32_t buf[8]; - uint32_t full_width = width * h_repeat; + uint32_t full_width = display_width * h_repeat; buf[0] = 0x4F434950; buf[1] = 0x01000101 + ((uint32_t)v_repeat << 16); buf[2] = full_width << 16; - buf[3] = (uint32_t)height << 16; + buf[3] = (uint32_t)display_height << 16; buf[4] = 0x00000001; - buf[5] = 0x00010000 + height + (bank << 24); + buf[5] = 0x00010000 + display_height + (bank << 24); buf[6] = 0x00000001; ram.write(0, buf, 7 * 4); ram.wait_for_finish_blocking(); @@ -375,13 +389,31 @@ namespace pimoroni { uint addr = 4 * 7; uint line_type = 0x80000000u + ((uint)mode << 28); mp_printf(&mp_plat_print, "Write header, line type %08x\n", line_type); - for (int i = 0; i < height; i += 8) { + for (int i = 0; i < display_height; i += 8) { for (int j = 0; j < 8; ++j) { - buf[j] = line_type + ((uint32_t)h_repeat << 24) + ((i + j) * width * 6) + base_address; + buf[j] = line_type + ((uint32_t)h_repeat << 24) + ((i + j) * frame_width * 6) + base_address; } ram.write(addr, buf, 8 * 4); ram.wait_for_finish_blocking(); addr += 4 * 8; } } + + uint32_t DVDisplay::point_to_address(const Point& p) const { + switch(mode) { + default: + case MODE_RGB555: return point_to_address16(p); + case MODE_PALETTE: return point_to_address_palette(p); + case MODE_RGB888: return point_to_address24(p); + } + } + + int DVDisplay::pixel_size() const { + switch(mode) { + default: + case MODE_RGB555: return 2; + case MODE_PALETTE: return 1; + case MODE_RGB888: return 3; + } + } } diff --git a/drivers/dv_display/dv_display.hpp b/drivers/dv_display/dv_display.hpp index 6527c1a0..0401e1c8 100644 --- a/drivers/dv_display/dv_display.hpp +++ b/drivers/dv_display/dv_display.hpp @@ -20,8 +20,8 @@ namespace pimoroni { static constexpr int PALETTE_SIZE = 32; enum Mode { - MODE_RGB555 = 1, MODE_PALETTE = 2, + MODE_RGB555 = 1, MODE_RGB888 = 3, }; @@ -41,6 +41,7 @@ namespace pimoroni { static constexpr uint I2C_REG_GPIO_HI_PULL_UP = 0xCB; static constexpr uint I2C_REG_GPIO_HI_PULL_DOWN = 0xCC; static constexpr uint I2C_REG_EDID = 0xED; + static constexpr uint I2C_REG_SCROLL = 0xF0; //-------------------------------------------------- // Variables @@ -59,8 +60,10 @@ namespace pimoroni { static constexpr uint RAM_SEL = 8; static constexpr uint32_t base_address = 0x10000; - uint16_t width = 0; - uint16_t height = 0; + uint16_t display_width = 0; + uint16_t display_height = 0; + uint16_t frame_width = 0; + uint16_t frame_height = 0; uint8_t bank = 0; uint8_t h_repeat = 1; uint8_t v_repeat = 1; @@ -127,7 +130,7 @@ namespace pimoroni { void write_pixel(const Point &p, RGB888 colour) override; void write_pixel_span(const Point &p, uint l, RGB888 colour) override; - void init(uint16_t width, uint16_t height, Mode mode = MODE_RGB555); + void init(uint16_t width, uint16_t height, Mode mode = MODE_RGB555, uint16_t frame_width = 0, uint16_t frame_height = 0); void flip(); void reset(); @@ -137,12 +140,18 @@ namespace pimoroni { void set_mode(Mode new_mode); void set_palette(RGB888 palette[PALETTE_SIZE]); void set_palette_colour(uint8_t entry, RGB888 colour); - + void write_palette_pixel(const Point &p, uint8_t colour); void write_palette_pixel_span(const Point &p, uint l, uint8_t colour); void write_palette_pixel_span(const Point &p, uint l, uint8_t* data); void read_palette_pixel_span(const Point &p, uint l, uint8_t *data); + // Set the top left corner of the display within the frame, if a larger + // frame is specified than the display. + // Note that the supplied x coordinate is rounded down to the previous multiple of 2 in RGB555 mode + // or multiple of 4 in palette or RGB888 modes + void set_display_offset(const Point& p); + uint8_t get_gpio(); uint8_t get_gpio_hi(); void set_gpio_hi_dir(uint pin, bool output); @@ -187,16 +196,19 @@ namespace pimoroni { void read(uint32_t address, size_t len, uint8_t *data); void write(uint32_t address, size_t len, const RGB888 colour); - uint32_t point_to_address(const Point &p) { - return base_address + ((p.y * (uint32_t)width * 3) + p.x) * 2; + uint32_t point_to_address(const Point& p) const; + int pixel_size() const; + + uint32_t point_to_address16(const Point &p) const { + return base_address + ((p.y * (uint32_t)frame_width * 3) + p.x) * 2; } - uint32_t point_to_address_palette(const Point &p) { - return base_address + (p.y * (uint32_t)width * 6) + p.x; + uint32_t point_to_address_palette(const Point &p) const { + return base_address + (p.y * (uint32_t)frame_width * 6) + p.x; } - uint32_t point_to_address24(const Point &p) { - return base_address + ((p.y * (uint32_t)width * 2) + p.x) * 3; + uint32_t point_to_address24(const Point &p) const { + return base_address + ((p.y * (uint32_t)frame_width * 2) + p.x) * 3; } }; } diff --git a/examples/dv_stick/dv_stick_test.cpp b/examples/dv_stick/dv_stick_test.cpp index df60681b..225658c9 100644 --- a/examples/dv_stick/dv_stick_test.cpp +++ b/examples/dv_stick/dv_stick_test.cpp @@ -9,8 +9,11 @@ using namespace pimoroni; -#define FRAME_WIDTH 360 -#define FRAME_HEIGHT 480 +#define DISPLAY_WIDTH 720 +#define DISPLAY_HEIGHT 480 + +#define FRAME_WIDTH 720 +#define FRAME_HEIGHT 720 #define READ_EDID 0 #if READ_EDID @@ -49,7 +52,7 @@ int main() { //sleep_ms(5000); DVDisplay display; - display.init(FRAME_WIDTH, FRAME_HEIGHT, DVDisplay::MODE_RGB888); + display.init(DISPLAY_WIDTH, DISPLAY_HEIGHT, DVDisplay::MODE_RGB888, FRAME_WIDTH, FRAME_HEIGHT); //display.test(); #if READ_EDID @@ -97,6 +100,9 @@ int main() { printf("Starting\n"); graphics.set_font("bitmap8"); + Point scroll = {0, 0}; + int scroll_dir = 1; + constexpr int NUM_CIRCLES = 50; struct Circle { uint16_t x, y, size, grow; @@ -198,6 +204,13 @@ int main() { //printf("%02x %02x\n", display.get_gpio(), display.get_gpio_hi()); + display.set_display_offset(scroll); + scroll.y += scroll_dir; + if (scroll.y + DISPLAY_HEIGHT > FRAME_HEIGHT || scroll.y < 0) { + scroll_dir = -scroll_dir; + scroll.y += scroll_dir; + } + ++frames; display.set_gpio_hi_pull_up_all(frames & 0x3F); display.set_gpio_hi_pull_down_all(~(frames & 0x3F));