From 90d3ab8178e8f4d48c71628b2d03dad92c8a90ea Mon Sep 17 00:00:00 2001 From: Jonathan Williamson Date: Sat, 16 Jan 2021 12:26:03 +0000 Subject: [PATCH] pico display driver --- drivers/st7789/st7789.cpp | 203 +++++++++++----------- drivers/st7789/st7789.hpp | 33 ++-- libraries/pico_graphics/pico_graphics.cpp | 92 ++++++++++ libraries/pico_graphics/pico_graphics.hpp | 33 ++++ pack/display/CMakeLists.txt | 2 + pack/display/demo.cpp | 182 ++++++++----------- pack/display/pico_display.cpp | 69 ++++++++ pack/display/pico_display.hpp | 27 +++ 8 files changed, 417 insertions(+), 224 deletions(-) create mode 100644 libraries/pico_graphics/pico_graphics.cpp create mode 100644 libraries/pico_graphics/pico_graphics.hpp create mode 100644 pack/display/pico_display.cpp create mode 100644 pack/display/pico_display.hpp diff --git a/drivers/st7789/st7789.cpp b/drivers/st7789/st7789.cpp index 638025ab..352c802f 100644 --- a/drivers/st7789/st7789.cpp +++ b/drivers/st7789/st7789.cpp @@ -1,10 +1,15 @@ #include "st7789.hpp" +#include +#include + #include "hardware/dma.h" +#include "hardware/pwm.h" namespace pimoroni { - void ST7789::init() { + void ST7789::init(bool auto_init_sequence) { + // configure spi interface and pins spi_init(spi, spi_baud); gpio_set_function(dc, GPIO_FUNC_SIO); @@ -13,23 +18,6 @@ namespace pimoroni { gpio_set_function(cs, GPIO_FUNC_SIO); gpio_set_dir(cs, GPIO_OUT); - // if framesync is enabled then the fs pin is toggled - // high during vsync - if(vsync != -1) { - gpio_set_function(fs, GPIO_FUNC_SIO); - gpio_set_dir(fs, GPIO_IN); - gpio_set_pulls(fs, false, true) - } - - // if a backlight pin is provided then set it up for - // pwm control - if(bl != -1) { - pwm_config cfg = pwm_get_default_config(); - pwm_config_wrap(&cfg, 65535); - pwm_init(pwm_gpio_to_slice(bl), &cfg, true); - gpio_set_function(bl, GPIO_FUNC_PWM); - } - gpio_set_function(sck, GPIO_FUNC_SPI); gpio_set_function(mosi, GPIO_FUNC_SPI); @@ -37,104 +25,111 @@ namespace pimoroni { gpio_set_function(miso, GPIO_FUNC_SPI); } + // if supported by the display then the vsync pin is + // toggled high during vertical blanking period + if(vsync != -1) { + gpio_set_function(vsync, GPIO_FUNC_SIO); + gpio_set_dir(vsync, GPIO_IN); + gpio_set_pulls(vsync, false, true); + } + + // if a backlight pin is provided then set it up for + // pwm control + if(bl != -1) { + pwm_config cfg = pwm_get_default_config(); + pwm_set_wrap(pwm_gpio_to_slice_num(bl), 65535); + pwm_init(pwm_gpio_to_slice_num(bl), &cfg, true); + gpio_set_function(bl, GPIO_FUNC_PWM); + } + + // if auto_init_sequence then send initialisation sequence + // for our standard displays based on the width and height + if(auto_init_sequence) { + command(reg::SWRESET); + + sleep_ms(150); + + if(width == 240 && height == 240) { + command(reg::MADCTL, 1, "\x04"); // row/column addressing order - rgb pixel order + command(reg::TEON, 1, "\x00"); // enable frame sync signal if used + command(reg::COLMOD, 1, "\x05"); // 16 bits per pixel + }else if(width == 240 && height == 135) { + command(reg::MADCTL, 1, "\x70"); + command(reg::COLMOD, 1, "\x05"); + } + + command(reg::INVON); // set inversion mode + command(reg::SLPOUT); // leave sleep mode + command(reg::DISPON); // turn display on + + sleep_ms(100); + + // setup correct addressing window + if(width == 240 && height == 240) { + command(reg::CASET, 4, "\x00\x00\x00\xef"); // 0 .. 239 columns + command(reg::RASET, 4, "\x00\x00\x00\xef"); // 0 .. 239 rows + }else if(width == 240 && height == 135) { + command(reg::RASET, 4, "\x00\x35\x00\xbb"); // 53 .. 187 (135 rows) + command(reg::CASET, 4, "\x00\x28\x01\x17"); // 40 .. 279 (240 columns) + } + } + + // the dma transfer works but without vsync it's not that useful as you could + // be updating the framebuffer during transfer... + // + // this could be avoided by creating another buffer to draw into and flip + // buffers (but costs another ~100kb of ram) + // + // it's probably not worth it for this particular usecase but will consider it + // some more... + + // setup spi for 16-bit transfers + // spi_set_format(spi, 16, SPI_CPOL_0, SPI_CPHA_0, SPI_MSB_FIRST); + // initialise dma channel for transmitting pixel data to screen - dma_channel = dma_claim_unused_channel(true); - dma_channel_config config = dma_channel_get_default_config(dma_channel); - //channel_config_set_bswap(&config, true); // byte swap to reverse little endian - - dma_channel_configure( - dma_channel, - &config, - &spi_get_hw(spi)->dr, - buffer, - sizeof(uint16_t) * width * height, - false); + // 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_16); + // channel_config_set_dreq(&config, spi_get_index(spi) ? DREQ_SPI1_TX : DREQ_SPI0_TX); + // dma_channel_configure( + // dma_channel, &config, &spi_get_hw(spi)->dr, frame_buffer, width * height, false); } - void ST7789::init_240x240() { - init(); + void ST7789::command(uint8_t command, size_t len, const char *data) { + //dma_channel_wait_for_finish_blocking(dma_channel); - width = 240; - height = 240; - row_stride = width * sizeof(uint16_t); + gpio_put(cs, 0); - // only initialise the buffer if one hasn't been supplied - if(!buffer) { - buffer = malloc(sizeof(uint16_t) * width * height); - } - - // initialise our standard 240x240 LCD as 16-bit colour - // with an RGB subpixel element order. - command(register::SWRESET); - - sleep_ms(150); - - command(register::MADCTL, 1, "\x04"); // row/column addressing order - rgb pixel order - command(register::TEON, 1, "\x00"); // enable frame sync signal if used - command(register::COLMOD, 1, "\x05"); // 16 bits per pixel - - command(register::SLPOUT); // leave sleep mode - command(register::DISPON); // turn display on - - sleep_ms(100); - - // set writing "window" to the full display - command(register::CASET, 4, "\x00\x00\x00\xef"); // 0 .. 239 columns - command(register::RASET, 4, "\x00\x00\x00\xef"); // 0 .. 239 rows - - // put into write mode - command(register::RAMWR); - } - - void ST7789::init_240x135() { - init(); - - width = 240; - height = 135; - row_stride = width * sizeof(uint16_t); - - // only initialise the buffer if one hasn't been supplied - if(!buffer) { - buffer = malloc(sizeof(uint16_t) * width * height); - } - - // initialise our standard 240x135 LCD as 16-bit colour - // with an RGB subpixel element order. - command(register::SWRESET); - - sleep_ms(150); - - command(register::MADCTL, 1, "\x70"); - command(register::COLMOD, 1, "\x05"); - - command(register::SLPOUT); // leave sleep mode - command(register::DISPON); // turn display on - - sleep_ms(100); - - command(register::RASET, 4, "\x00\x35\x00\xbb"); // 53 .. 187 (135 rows) - command(register::CASET, 4, "\x00\x28\x01\x17"); // 40 .. 279 (240 columns) - } - - void ST7789::command(uint8_t command, size_t len = 0, const char *data = NULL) { - gpio_put(pin::CS, 0); - - gpio_put(pin::DC, 0); // command mode - spi_write_blocking(spi0, &command, 1); + gpio_put(dc, 0); // command mode + spi_write_blocking(spi, &command, 1); if(data) { - gpio_put(pin::DC, 1); // data mode - spi_write_blocking(spi0, (const uint8_t*)data, len); + gpio_put(dc, 1); // data mode + spi_write_blocking(spi, (const uint8_t*)data, len); } - gpio_put(pin::CS, 1); + gpio_put(cs, 1); } - void ST7789::update() { + void ST7789::update(bool dont_block) { + ST7789::command(reg::RAMWR, width * height * sizeof(uint16_t), (const char*)frame_buffer); + + /*if(dma_channel_is_busy(dma_channel) && dont_block) { + return; + } + dma_channel_wait_for_finish_blocking(dma_channel); - command(register::RAMWR); - dma_channel_start(dma_channel); + uint8_t r = reg::RAMWR; + + gpio_put(cs, 0); + + gpio_put(dc, 0); // command mode + spi_write_blocking(spi, &r, 1); + + gpio_put(dc, 1); // data mode + + dma_channel_set_read_addr(dma_channel, frame_buffer, true);*/ } void ST7789::set_backlight(uint8_t brightness) { @@ -146,6 +141,6 @@ namespace pimoroni { } void ST7789::vsync_callback(gpio_irq_callback_t callback) { - gpio_set_irq_enabled_with_callback(vsync, GPIO_IRQ_EDGE_RISE, true, &callback); + gpio_set_irq_enabled_with_callback(vsync, GPIO_IRQ_EDGE_RISE, true, callback); } } \ No newline at end of file diff --git a/drivers/st7789/st7789.hpp b/drivers/st7789/st7789.hpp index 10faabb0..a2ee83ff 100644 --- a/drivers/st7789/st7789.hpp +++ b/drivers/st7789/st7789.hpp @@ -1,14 +1,13 @@ #pragma once #include "hardware/spi.h" +#include "hardware/gpio.h" namespace pimoroni { class ST7789 { - spi_inst_t *spi = spi0_hw; + spi_inst_t *spi = spi0; - // framebuffer for storing pixel data - uint16_t *buffer; uint32_t dma_channel; // screen properties @@ -28,18 +27,28 @@ namespace pimoroni { uint32_t spi_baud = 64 * 1024 * 1024; public: - ST7789(spi_inst_t *spi, uint8_t cs, uint8_t dc, uint8_t sck, uint8_t mosi, uint8_t miso = -1) : - spi(spi), cs(cs), dc(dc), sck(sck), mosi(mosi), miso(miso) {} + // frame buffer where pixel data is stored + uint16_t *frame_buffer; - void init(); - void init_240x240(); - void init_240x235(); + public: + ST7789(uint16_t width, uint16_t height, uint16_t *frame_buffer) : + width(width), height(height), frame_buffer(frame_buffer) {} + + ST7789(uint16_t width, uint16_t height, uint16_t *frame_buffer, + spi_inst_t *spi, + uint8_t cs, uint8_t dc, uint8_t sck, uint8_t mosi, uint8_t miso = -1) : + width(width), height(height), frame_buffer(frame_buffer), + spi(spi), + cs(cs), dc(dc), sck(sck), mosi(mosi), miso(miso) {} + + void init(bool auto_init_sequence = true); + + void command(uint8_t command, size_t len = 0, const char *data = NULL); void vsync_callback(gpio_irq_callback_t callback); - void update(); + void update(bool dont_block = false); + void set_backlight(uint8_t brightness); - uint16_t pen(uint8_t r, uint8_t g, uint8_t b); - - enum register { + enum reg { SWRESET = 0x01, TEON = 0x35, MADCTL = 0x36, diff --git a/libraries/pico_graphics/pico_graphics.cpp b/libraries/pico_graphics/pico_graphics.cpp new file mode 100644 index 00000000..da618414 --- /dev/null +++ b/libraries/pico_graphics/pico_graphics.cpp @@ -0,0 +1,92 @@ +#include "pico_graphics.hpp" + +namespace pimoroni { + + void PicoGraphics::set_pen(uint8_t r, uint8_t g, uint8_t b) { + this->pen = create_pen(r, g, b); + } + + void PicoGraphics::set_pen(uint16_t pen) { + this->pen = pen; + } + + uint16_t PicoGraphics::create_pen(uint8_t r, uint8_t g, uint8_t b) { + uint16_t p = ((r & 0b11111000) << 8) | + ((g & 0b11111100) << 3) | + ((b & 0b11111000) >> 3); + + // endian swap, this should be possible another way... + return ((p & 0xff00) >> 8) | ((p & 0xff) << 8); + } + + uint16_t* PicoGraphics::ptr(int32_t x, int32_t y) { + return this->frame_buffer + x + y * this->width; + } + + void PicoGraphics::pixel(int32_t x, int32_t y) { + if(x < 0 || y < 0 || x >= this->width || y >= this->height) { return; } + + *ptr(x, y) = pen; + } + + void PicoGraphics::pixel_span(int32_t x, int32_t y, int32_t l) { + if(x + l < 0 || y < 0 || x >= this->width || y >= this->height) { return; } + + if(x < 0) {l += x; x = 0;} + if(x + l >= this->width) {l = this->width - x;} + + uint16_t *p = ptr(x, y); + while(l--) { + *p++ = pen; + } + } + + void PicoGraphics::rectangle(int32_t x, int32_t y, int32_t width, int32_t height) { + // clip and/or discard depending on rectangle visibility + if(x >= this->width || y >= this->height) { return; } + if(x < 0) { width += x; x = 0; } + if(y < 0) { height += y; y = 0; } + if(width <= 0 || height <= 0) { return; } + if(x + width >= this->width) { width = this->width - x; } + if(y + height >= this->height) { height = this->height - y; } + + uint16_t *p = ptr(x, y); + while(height--) { + for(uint32_t i = 0; i < width; i++) { + *p++ = this->pen; + } + p += this->width - width; // move to next scanline + } + } + + void PicoGraphics::circle(int32_t x, int32_t y, int32_t radius) { + // circle in screen bounds? + if(x + radius < 0 || y + radius < 0 || x - radius >= this->width || y - radius >= this->height) { return; } + + int ox = radius, oy = 0, err = -radius; + while (ox >= oy) + { + int last_oy = oy; + + err += oy; oy++; err += oy; + + pixel_span(x - ox, y + last_oy, ox * 2 + 1); + if (last_oy != 0) { + pixel_span(x - ox, y - last_oy, ox * 2 + 1); + } + + if (err >= 0) { + if (ox != last_oy) { + pixel_span(x - last_oy, y + ox, last_oy * 2 + 1); + if (ox != 0) { + pixel_span(x - last_oy, y - ox, last_oy * 2 + 1); + } + + err -= ox; ox--; err -= ox; + } + } + } + } + + +} \ No newline at end of file diff --git a/libraries/pico_graphics/pico_graphics.hpp b/libraries/pico_graphics/pico_graphics.hpp new file mode 100644 index 00000000..7c319517 --- /dev/null +++ b/libraries/pico_graphics/pico_graphics.hpp @@ -0,0 +1,33 @@ +#pragma once + +#include + +// a tiny little graphics library for our Pico products +// supports only 16-bit (565) RGB framebuffers +namespace pimoroni { + + class PicoGraphics { + public: + uint16_t width; + uint16_t height; + uint16_t *frame_buffer; + uint16_t pen; + + public: + PicoGraphics(uint16_t width, uint16_t height, uint16_t *frame_buffer) + : frame_buffer(frame_buffer), width(width), height(height) {} + + void set_pen(uint8_t r, uint8_t g, uint8_t b); + void set_pen(uint16_t p); + uint16_t create_pen(uint8_t r, uint8_t g, uint8_t b); + + uint16_t* ptr(int32_t x, int32_t y); + + void pixel(int32_t x, int32_t y); + void pixel_span(int32_t x, int32_t y, int32_t l); + void rectangle(int32_t x, int32_t y, int32_t w, int32_t h); + void circle(int32_t x, int32_t y, int32_t r); + //void polygon(std::vector); + }; + +} \ No newline at end of file diff --git a/pack/display/CMakeLists.txt b/pack/display/CMakeLists.txt index 08c250dd..9bfdeb56 100644 --- a/pack/display/CMakeLists.txt +++ b/pack/display/CMakeLists.txt @@ -14,7 +14,9 @@ add_executable( display demo.cpp image_data.cpp + pico_display.cpp ../../../drivers/st7789/st7789.cpp + ../../../libraries/pico_graphics/pico_graphics.cpp ) # Pull in pico libraries that we need diff --git a/pack/display/demo.cpp b/pack/display/demo.cpp index c06f74f6..8ef199bf 100644 --- a/pack/display/demo.cpp +++ b/pack/display/demo.cpp @@ -1,49 +1,33 @@ #include #include +#include +#include -#include "hardware/pwm.h" -#include "../../drivers/st7789/st7789.hpp" +#include "pico_display.hpp" using namespace pimoroni; extern unsigned char image_tif[]; extern unsigned int image_tif_len; -enum pin { - LED_R = 6, - LED_G = 7, - LED_B = 8, - BUTTON_A = 12, - BUTTON_B = 13, - BUTTON_X = 14, - BUTTON_Y = 15 -}; - - -ST7789 screen; - -void flip() { - // write to screen -} - - +PicoDisplay pico_display; +/* void pixel(int x, int y, uint16_t c) { x *= 2; y *= 2; - screen.buffer[x + y * 240] = c; - screen.buffer[x + 1 + y * 240] = c; - screen.buffer[x + 1 + (y + 1) * 240] = c; - screen.buffer[x + (y + 1) * 240] = c; + pico_display.frame_buffer[x + y * 240] = c; + pico_display.frame_buffer[x + 1 + y * 240] = c; + pico_display.frame_buffer[x + 1 + (y + 1) * 240] = c; + pico_display.frame_buffer[x + (y + 1) * 240] = c; } - void rect(int x, int y, int w, int h, uint16_t c) { for(int rx = x; rx < x + w; rx++) { for(int ry = y; ry < y + h; ry++) { pixel(rx, ry, c); } } -} +}*/ uint8_t arrow[] = { 0b00010000, @@ -67,7 +51,7 @@ uint8_t tick[] = { 0b00000000, }; - +/* void sprite(uint8_t *p, int x, int y, bool flip, uint16_t c) { for(int ay = 0; ay < 8; ay++) { uint8_t sl = p[ay]; @@ -83,116 +67,98 @@ void sprite(uint8_t *p, int x, int y, bool flip, uint16_t c) { } } } -} +}*/ int main() { + pico_display.set_backlight(100); - //screen.buffer = (uint16_t*)(image_tif + 292); - screen.init_240x135(); - - screen.set_backlight(100); - - // rgb led to a soft white - pwm_config cfg1 = pwm_get_default_config(); - pwm_config_set_wrap(&cfg1, 255); - pwm_config_set_output_polarity(&cfg1, true, true); - pwm_init(pwm_gpio_to_slice_num(pin::LED_R), &cfg1, true); - gpio_set_function(pin::LED_R, GPIO_FUNC_PWM); - pwm_set_gpio_level(pin::LED_R, 50); - - pwm_config cfg2 = pwm_get_default_config(); - pwm_config_set_wrap(&cfg2, 255); - pwm_config_set_output_polarity(&cfg2, true, true); - pwm_init(pwm_gpio_to_slice_num(pin::LED_G), &cfg2, true); - gpio_set_function(pin::LED_G, GPIO_FUNC_PWM); - pwm_set_gpio_level(pin::LED_G, 50); - - pwm_config cfg3 = pwm_get_default_config(); - pwm_config_set_wrap(&cfg3, 255); - pwm_config_set_output_polarity(&cfg3, true, true); - pwm_init(pwm_gpio_to_slice_num(pin::LED_B), &cfg3, true); - gpio_set_function(pin::LED_B, GPIO_FUNC_PWM); - pwm_set_gpio_level(pin::LED_B, 50); - - // setup button inputs - gpio_set_function(pin::BUTTON_A, GPIO_FUNC_SIO); gpio_set_dir(pin::BUTTON_A, GPIO_IN); gpio_pull_up(pin::BUTTON_A); - gpio_set_function(pin::BUTTON_B, GPIO_FUNC_SIO); gpio_set_dir(pin::BUTTON_B, GPIO_IN); gpio_pull_up(pin::BUTTON_B); - gpio_set_function(pin::BUTTON_X, GPIO_FUNC_SIO); gpio_set_dir(pin::BUTTON_X, GPIO_IN); gpio_pull_up(pin::BUTTON_X); - gpio_set_function(pin::BUTTON_Y, GPIO_FUNC_SIO); gpio_set_dir(pin::BUTTON_Y, GPIO_IN); gpio_pull_up(pin::BUTTON_Y); - - uint16_t white = screen.pen(255, 255, 255); - uint16_t black = screen.pen(0, 0, 0); - uint16_t red = screen.pen(255, 0, 0); - uint16_t green = screen.pen(0, 255, 0); - uint16_t dark_grey = screen.pen(20, 40, 60); - uint16_t dark_green = screen.pen(10, 100, 10); - uint16_t blue = screen.pen(0, 0, 255); + // uint16_t white = pico_display.create_pen(255, 255, 255); + // uint16_t black = pico_display.create_pen(0, 0, 0); + // uint16_t red = pico_display.create_pen(255, 0, 0); + // uint16_t green = pico_display.create_pen(0, 255, 0); + // uint16_t dark_grey = pico_display.create_pen(20, 40, 60); + // uint16_t dark_green = pico_display.create_pen(10, 100, 10); + // uint16_t blue = pico_display.create_pen(0, 0, 255); bool a_pressed = false; bool b_pressed = false; bool x_pressed = false; bool y_pressed = false; + struct pt { + float x; + float y; + uint8_t r; + float dx; + float dy; + uint16_t pen; + }; + + std::vector shapes; + for(int i = 0; i < 1000; i++) { + pt shape; + shape.x = rand() % 240; + shape.y = rand() % 135; + shape.r = (rand() % 10) + 3; + shape.dx = float(rand() % 255) / 128.0f; + shape.dy = float(rand() % 255) / 128.0f; + shape.pen = pico_display.create_pen(rand() % 255, rand() % 255, rand() % 255); + shapes.push_back(shape); + } + uint32_t i = 0; while(true) { - //rect(0, 0, 120, 67, dark_grey); - //memset(framebuffer, 0x05, 240 * 135 * 2); + pico_display.set_pen(120, 40, 60); + pico_display.rectangle(0, 0, 240, 135); - // we need to be careful to clamp the input to - // sin here because otherwise the fast float - // library will choke - float sin_step = fmod(i / 5.0f, M_PI * 2.0f); - int bounce = sin(sin_step) * 4.0f; + for(auto &shape : shapes) { + shape.x += shape.dx; + shape.y += shape.dy; + if(shape.x < 0) shape.dx *= -1; + if(shape.x >= pico_display.width) shape.dx *= -1; + if(shape.y < 0) shape.dy *= -1; + if(shape.y >= pico_display.height) shape.dy *= -1; + pico_display.set_pen(shape.pen); + pico_display.circle(shape.x, shape.y, shape.r); + } float led_step = fmod(i / 20.0f, M_PI * 2.0f); int r = (sin(led_step) * 25.0f) + 25.0f; - // int g = (sin(led_step + 2.09f) * 25.0f) + 25.0f; - // int b = (sin(led_step + 4.18f) * 25.0f) + 25.0f; - // pwm_set_gpio_level(pin::LED_R, r); - // pwm_set_gpio_level(pin::LED_G, g); - // pwm_set_gpio_level(pin::LED_B, b); + pico_display.set_led(r, r / 1.2f, r); - pwm_set_gpio_level(pin::LED_R, r); - pwm_set_gpio_level(pin::LED_G, r / 1.2f); - pwm_set_gpio_level(pin::LED_B, r); - - if(!gpio_get(pin::BUTTON_A)) { a_pressed = true; } - if(!gpio_get(pin::BUTTON_B)) { b_pressed = true; } - if(!gpio_get(pin::BUTTON_X)) { x_pressed = true; } - if(!gpio_get(pin::BUTTON_Y)) { y_pressed = true; } - - if(a_pressed) { - rect(0, 0, 18, 18, dark_green); - sprite(tick, 5, 5, true, green); +/* + if(pico_display.is_pressed(pico_display.A)) { + pico_display.rectangle(0, 0, 18, 18); + //sprite(tick, 5, 5, true, green); }else{ - sprite(arrow, 10 + bounce, 10, true, white); + //sprite(arrow, 10 + bounce, 10, true, white); } - if(b_pressed) { - rect(0, 49, 18, 18, dark_green); - sprite(tick, 5, 54, true, green); + + if(pico_display.is_pressed(pico_display.B)) { + pico_display.rectangle(0, 49, 18, 18); + //sprite(tick, 5, 54, true, green); }else{ - sprite(arrow, 10 - bounce, 50, true, white); + //sprite(arrow, 10 - bounce, 50, true, white); } - if(x_pressed) { - rect(102, 0, 18, 18, dark_green); - sprite(tick, 107, 5, true, green); + if(pico_display.is_pressed(pico_display.X)) { + pico_display.rectangle(102, 0, 18, 18); + //sprite(tick, 107, 5, true, green); }else{ - sprite(arrow, 102 - bounce, 10, false, white); + //sprite(arrow, 102 - bounce, 10, false, white); } - if(y_pressed) { - rect(102, 49, 18, 18, dark_green); - sprite(tick, 107, 54, true, green); + if(pico_display.is_pressed(pico_display.Y)) { + pico_display.rectangle(102, 49, 18, 18); + //sprite(tick, 107, 54, true, green); }else{ - sprite(arrow, 102 + bounce, 50, false, white); + //sprite(arrow, 102 + bounce, 50, false, white); } - +*/ // update screen - // switch st7789 into data mode - screen.update(); + pico_display.update(); i++; } diff --git a/pack/display/pico_display.cpp b/pack/display/pico_display.cpp new file mode 100644 index 00000000..c8906c1d --- /dev/null +++ b/pack/display/pico_display.cpp @@ -0,0 +1,69 @@ +#include + +#include "hardware/pwm.h" + +#include "pico_display.hpp" + +const uint8_t LED_R = 6; +const uint8_t LED_G = 7; +const uint8_t LED_B = 8; + +namespace pimoroni { + + PicoDisplay::PicoDisplay() + : screen(240, 135, __fb), PicoGraphics(240, 135, __fb) { + + // setup the rgb led for pwm control + pwm_config cfg = pwm_get_default_config(); + pwm_config_set_output_polarity(&cfg, true, true); + + // red + pwm_set_wrap(pwm_gpio_to_slice_num(LED_R), 65535); + pwm_init(pwm_gpio_to_slice_num(LED_R), &cfg, true); + gpio_set_function(LED_R, GPIO_FUNC_PWM); + + // green + pwm_set_wrap(pwm_gpio_to_slice_num(LED_G), 65535); + pwm_init(pwm_gpio_to_slice_num(LED_G), &cfg, true); + gpio_set_function(LED_G, GPIO_FUNC_PWM); + + // blue + pwm_set_wrap(pwm_gpio_to_slice_num(LED_B), 65535); + pwm_init(pwm_gpio_to_slice_num(LED_B), &cfg, true); + gpio_set_function(LED_B, GPIO_FUNC_PWM); + + // setup button inputs + gpio_set_function(A, GPIO_FUNC_SIO); gpio_set_dir(A, GPIO_IN); gpio_pull_up(A); + gpio_set_function(B, GPIO_FUNC_SIO); gpio_set_dir(B, GPIO_IN); gpio_pull_up(B); + gpio_set_function(X, GPIO_FUNC_SIO); gpio_set_dir(X, GPIO_IN); gpio_pull_up(X); + gpio_set_function(Y, GPIO_FUNC_SIO); gpio_set_dir(Y, GPIO_IN); gpio_pull_up(Y); + + // initialise the screen + screen.init(); + } + + void PicoDisplay::set_led(uint8_t r, uint8_t g, uint8_t b) { + // gamma correct the provided 0-255 brightness value onto a + // 0-65535 range for the pwm counter + static const float gamma = 2.8; + + uint16_t value; + + // red + value = (uint16_t)(pow((float)(r) / 255.0f, gamma) * 65536.0f + 0.5f); + pwm_set_gpio_level(LED_R, value); + + // green + value = (uint16_t)(pow((float)(g) / 255.0f, gamma) * 65536.0f + 0.5f); + pwm_set_gpio_level(LED_G, value); + + // blue + value = (uint16_t)(pow((float)(b) / 255.0f, gamma) * 65536.0f + 0.5f); + pwm_set_gpio_level(LED_B, value); + } + + bool PicoDisplay::is_pressed(uint8_t button) { + return !gpio_get(button); + } + +} \ No newline at end of file diff --git a/pack/display/pico_display.hpp b/pack/display/pico_display.hpp new file mode 100644 index 00000000..b1c53d66 --- /dev/null +++ b/pack/display/pico_display.hpp @@ -0,0 +1,27 @@ +#pragma once + +#include "../../drivers/st7789/st7789.hpp" +#include "../../libraries/pico_graphics/pico_graphics.hpp" + +namespace pimoroni { + + class PicoDisplay : public PicoGraphics { + uint16_t __fb[240 * 135]; + ST7789 screen; + + public: + PicoDisplay(); + + void set_backlight(uint8_t brightness) {screen.set_backlight(brightness);} + void update() {screen.update();} + + void set_led(uint8_t r, uint8_t g, uint8_t b); + bool is_pressed(uint8_t button); + + static const uint8_t A = 12; + static const uint8_t B = 13; + static const uint8_t X = 14; + static const uint8_t Y = 15; + }; + +} \ No newline at end of file