diff --git a/.github/workflows/micropython.yml b/.github/workflows/micropython.yml index 9f9e007cc..55dd1d9bc 100644 --- a/.github/workflows/micropython.yml +++ b/.github/workflows/micropython.yml @@ -7,7 +7,8 @@ on: types: [created] env: - MICROPYTHON_VERSION: v1.23.0 + MICROPYTHON_VERSION: feature/psram + MICROPYTHON_FLAVOUR: pimoroni jobs: build: @@ -87,7 +88,7 @@ jobs: uses: actions/checkout@v4 with: repository: gadgetoid/py_decl - ref: v0.0.1 + ref: v0.0.4 path: py_decl - name: Build MPY Cross diff --git a/ci/micropython.sh b/ci/micropython.sh index 2f598c084..3f044e766 100644 --- a/ci/micropython.sh +++ b/ci/micropython.sh @@ -13,11 +13,9 @@ function log_warning { } function micropython_clone { - log_inform "Using MicroPython $MICROPYTHON_VERSION" - git clone https://github.com/micropython/micropython + log_inform "Using MicroPython $MICROPYTHON_FLAVOUR/$MICROPYTHON_VERSION" + git clone https://github.com/$MICROPYTHON_FLAVOUR/micropython -b $MICROPYTHON_VERSION --depth=1 cd micropython - git checkout $MICROPYTHON_VERSION - git cherry-pick -n 932f76c6ba64c5a3e68de3324556d9979f09303b git submodule update --init lib/pico-sdk git submodule update --init lib/cyw43-driver git submodule update --init lib/lwip diff --git a/drivers/st7735/st7735.cpp b/drivers/st7735/st7735.cpp index d2cabff7c..b945537a8 100644 --- a/drivers/st7735/st7735.cpp +++ b/drivers/st7735/st7735.cpp @@ -165,7 +165,7 @@ namespace pimoroni { // Native 16-bit framebuffer update void ST7735::update(PicoGraphics *graphics) { - if(graphics->pen_type == PicoGraphics::PEN_RGB565) { + if(graphics->pen_type == PicoGraphics::PEN_RGB565 && graphics->layers == 1) { command(reg::RAMWR, width * height * sizeof(uint16_t), (const char*)graphics->frame_buffer); } else { command(reg::RAMWR); diff --git a/drivers/st7789/st7789.cpp b/drivers/st7789/st7789.cpp index ed34447c5..9f033adc5 100644 --- a/drivers/st7789/st7789.cpp +++ b/drivers/st7789/st7789.cpp @@ -282,7 +282,7 @@ namespace pimoroni { void ST7789::update(PicoGraphics *graphics) { uint8_t cmd = reg::RAMWR; - if(graphics->pen_type == PicoGraphics::PEN_RGB565) { // Display buffer is screen native + if(graphics->pen_type == PicoGraphics::PEN_RGB565 && graphics->layers == 1) { // Display buffer is screen native command(cmd, width * height * sizeof(uint16_t), (const char*)graphics->frame_buffer); } else { gpio_put(dc, 0); // command mode diff --git a/drivers/st7789/st7789.hpp b/drivers/st7789/st7789.hpp index e4ad2ee6f..52369256f 100644 --- a/drivers/st7789/st7789.hpp +++ b/drivers/st7789/st7789.hpp @@ -58,9 +58,10 @@ namespace pimoroni { cs(pins.cs), dc(pins.dc), wr_sck(pins.wr_sck), rd_sck(pins.rd_sck), d0(pins.d0), bl(pins.bl) { parallel_pio = pio1; + pio_set_gpio_base(parallel_pio, d0 + 8 >= 32 ? 16 : 0); parallel_sm = pio_claim_unused_sm(parallel_pio, true); parallel_offset = pio_add_program(parallel_pio, &st7789_parallel_program); - + //gpio_init(wr_sck); //gpio_set_dir(wr_sck, GPIO_OUT); //gpio_set_function(wr_sck, GPIO_FUNC_SIO); diff --git a/drivers/uc8151/uc8151.cpp b/drivers/uc8151/uc8151.cpp index 76477141f..92d7a6203 100644 --- a/drivers/uc8151/uc8151.cpp +++ b/drivers/uc8151/uc8151.cpp @@ -389,7 +389,7 @@ namespace pimoroni { } void UC8151::power_off() { - command(POF); + //command(POF); } void UC8151::read(uint8_t reg, size_t len, uint8_t *data) { @@ -534,7 +534,7 @@ namespace pimoroni { void UC8151::off() { busy_wait(); - command(POF); // turn off + //command(POF); // turn off } } diff --git a/libraries/pico_graphics/pico_graphics.cpp b/libraries/pico_graphics/pico_graphics.cpp index 4e32e4ec3..c457ca99d 100644 --- a/libraries/pico_graphics/pico_graphics.cpp +++ b/libraries/pico_graphics/pico_graphics.cpp @@ -19,6 +19,14 @@ namespace pimoroni { RGB* PicoGraphics::get_palette() {return nullptr;} bool PicoGraphics::supports_alpha_blend() {return false;} + void PicoGraphics::set_layer(uint l) { + this->layer = l; + this->layer_offset = this->bounds.w * this->bounds.h * l; + }; + uint PicoGraphics::get_layer() { + return this->layer; + }; + void PicoGraphics::set_dimensions(int width, int height) { bounds = clip = {0, 0, width, height}; } diff --git a/libraries/pico_graphics/pico_graphics.hpp b/libraries/pico_graphics/pico_graphics.hpp index 7445c5342..cb0218457 100644 --- a/libraries/pico_graphics/pico_graphics.hpp +++ b/libraries/pico_graphics/pico_graphics.hpp @@ -79,6 +79,7 @@ namespace pimoroni { } } + constexpr operator bool() {return r || g || b;}; constexpr RGB operator+ (const RGB& c) const {return RGB(r + c.r, g + c.g, b + c.b);} constexpr RGB& operator+=(const RGB& c) {r += c.r; g += c.g; b += c.b; return *this;} constexpr RGB& operator-=(const RGB& c) {r -= c.r; g -= c.g; b -= c.b; return *this;} @@ -226,6 +227,10 @@ namespace pimoroni { Rect clip; uint thickness = 1; + uint layers = 1; + uint layer = 0; + uint layer_offset = 0; + typedef std::function conversion_callback_func; typedef std::function next_pixel_func; typedef std::function next_pixel_func_rgb888; @@ -270,6 +275,12 @@ namespace pimoroni { PicoGraphics(uint16_t width, uint16_t height, void *frame_buffer) : frame_buffer(frame_buffer), bounds(0, 0, width, height), clip(0, 0, width, height) { set_font(&font6); + layers = 1; + }; + + PicoGraphics(uint16_t width, uint16_t height, uint16_t layers, void *frame_buffer) + : frame_buffer(frame_buffer), bounds(0, 0, width, height), clip(0, 0, width, height), layers(layers) { + set_font(&font6); }; virtual void set_pen(uint c) = 0; @@ -278,6 +289,9 @@ namespace pimoroni { virtual void set_pixel_span(const Point &p, uint l) = 0; void set_thickness(uint t); + void set_layer(uint l); + uint get_layer(); + virtual int get_palette_size(); virtual RGB* get_palette(); virtual bool supports_alpha_blend(); @@ -330,7 +344,7 @@ namespace pimoroni { public: uint8_t color; - PicoGraphics_Pen1Bit(uint16_t width, uint16_t height, void *frame_buffer); + PicoGraphics_Pen1Bit(uint16_t width, uint16_t height, void *frame_buffer, uint16_t layers = 1); void set_pen(uint c) override; void set_pen(uint8_t r, uint8_t g, uint8_t b) override; @@ -346,7 +360,7 @@ namespace pimoroni { public: uint8_t color; - PicoGraphics_Pen1BitY(uint16_t width, uint16_t height, void *frame_buffer); + PicoGraphics_Pen1BitY(uint16_t width, uint16_t height, void *frame_buffer, uint16_t layers = 1); void set_pen(uint c) override; void set_pen(uint8_t r, uint8_t g, uint8_t b) override; @@ -387,7 +401,7 @@ namespace pimoroni { bool cache_built = false; std::array candidates; - PicoGraphics_Pen3Bit(uint16_t width, uint16_t height, void *frame_buffer); + PicoGraphics_Pen3Bit(uint16_t width, uint16_t height, void *frame_buffer, uint16_t layers = 1); void set_pen(uint c) override; void set_pen(uint8_t r, uint8_t g, uint8_t b) override; @@ -420,7 +434,7 @@ namespace pimoroni { bool cache_built = false; std::array candidates; - PicoGraphics_PenP4(uint16_t width, uint16_t height, void *frame_buffer); + PicoGraphics_PenP4(uint16_t width, uint16_t height, void *frame_buffer, uint16_t layers = 1); void set_pen(uint c) override; void set_pen(uint8_t r, uint8_t g, uint8_t b) override; int update_pen(uint8_t i, uint8_t r, uint8_t g, uint8_t b) override; @@ -453,7 +467,7 @@ namespace pimoroni { bool cache_built = false; std::array candidates; - PicoGraphics_PenP8(uint16_t width, uint16_t height, void *frame_buffer); + PicoGraphics_PenP8(uint16_t width, uint16_t height, void *frame_buffer, uint16_t layers = 1); void set_pen(uint c) override; void set_pen(uint8_t r, uint8_t g, uint8_t b) override; int update_pen(uint8_t i, uint8_t r, uint8_t g, uint8_t b) override; @@ -478,7 +492,7 @@ namespace pimoroni { class PicoGraphics_PenRGB332 : public PicoGraphics { public: RGB332 color; - PicoGraphics_PenRGB332(uint16_t width, uint16_t height, void *frame_buffer); + PicoGraphics_PenRGB332(uint16_t width, uint16_t height, void *frame_buffer, uint16_t layers = 1); void set_pen(uint c) override; void set_pen(uint8_t r, uint8_t g, uint8_t b) override; int create_pen(uint8_t r, uint8_t g, uint8_t b) override; @@ -503,7 +517,7 @@ namespace pimoroni { public: RGB src_color; RGB565 color; - PicoGraphics_PenRGB565(uint16_t width, uint16_t height, void *frame_buffer); + PicoGraphics_PenRGB565(uint16_t width, uint16_t height, void *frame_buffer, uint16_t layers = 1); void set_pen(uint c) override; void set_pen(uint8_t r, uint8_t g, uint8_t b) override; int create_pen(uint8_t r, uint8_t g, uint8_t b) override; @@ -513,13 +527,15 @@ namespace pimoroni { static size_t buffer_size(uint w, uint h) { return w * h * sizeof(RGB565); } + + void frame_convert(PenType type, conversion_callback_func callback) override; }; class PicoGraphics_PenRGB888 : public PicoGraphics { public: RGB src_color; RGB888 color; - PicoGraphics_PenRGB888(uint16_t width, uint16_t height, void *frame_buffer); + PicoGraphics_PenRGB888(uint16_t width, uint16_t height, void *frame_buffer, uint16_t layers = 1); void set_pen(uint c) override; void set_pen(uint8_t r, uint8_t g, uint8_t b) override; int create_pen(uint8_t r, uint8_t g, uint8_t b) override; @@ -597,7 +613,7 @@ namespace pimoroni { uint color; IDirectDisplayDriver &driver; - PicoGraphics_PenInky7(uint16_t width, uint16_t height, IDirectDisplayDriver &direct_display_driver); + PicoGraphics_PenInky7(uint16_t width, uint16_t height, IDirectDisplayDriver &direct_display_driver, uint16_t layers = 1); void set_pen(uint c) override; void set_pen(uint8_t r, uint8_t g, uint8_t b) override; int create_pen(uint8_t r, uint8_t g, uint8_t b) override; diff --git a/libraries/pico_graphics/pico_graphics_pen_1bit.cpp b/libraries/pico_graphics/pico_graphics_pen_1bit.cpp index 30fb1e53b..e05b2a370 100644 --- a/libraries/pico_graphics/pico_graphics_pen_1bit.cpp +++ b/libraries/pico_graphics/pico_graphics_pen_1bit.cpp @@ -2,11 +2,11 @@ namespace pimoroni { - PicoGraphics_Pen1Bit::PicoGraphics_Pen1Bit(uint16_t width, uint16_t height, void *frame_buffer) - : PicoGraphics(width, height, frame_buffer) { + PicoGraphics_Pen1Bit::PicoGraphics_Pen1Bit(uint16_t width, uint16_t height, void *frame_buffer, uint16_t layers) + : PicoGraphics(width, height, layers, frame_buffer) { this->pen_type = PEN_1BIT; if(this->frame_buffer == nullptr) { - this->frame_buffer = (void *)(new uint8_t[buffer_size(width, height)]); + this->frame_buffer = (void *)(new uint8_t[buffer_size(width, height) * layers]); } } diff --git a/libraries/pico_graphics/pico_graphics_pen_1bitY.cpp b/libraries/pico_graphics/pico_graphics_pen_1bitY.cpp index 6fc2cb8c2..6efdde920 100644 --- a/libraries/pico_graphics/pico_graphics_pen_1bitY.cpp +++ b/libraries/pico_graphics/pico_graphics_pen_1bitY.cpp @@ -2,8 +2,8 @@ namespace pimoroni { - PicoGraphics_Pen1BitY::PicoGraphics_Pen1BitY(uint16_t width, uint16_t height, void *frame_buffer) - : PicoGraphics(width, height, frame_buffer) { + PicoGraphics_Pen1BitY::PicoGraphics_Pen1BitY(uint16_t width, uint16_t height, void *frame_buffer, uint16_t layers) + : PicoGraphics(width, height, layers, frame_buffer) { this->pen_type = PEN_1BIT; if(this->frame_buffer == nullptr) { this->frame_buffer = (void *)(new uint8_t[buffer_size(width, height)]); diff --git a/libraries/pico_graphics/pico_graphics_pen_3bit.cpp b/libraries/pico_graphics/pico_graphics_pen_3bit.cpp index 5faba1cdc..dc67018b3 100644 --- a/libraries/pico_graphics/pico_graphics_pen_3bit.cpp +++ b/libraries/pico_graphics/pico_graphics_pen_3bit.cpp @@ -2,8 +2,8 @@ namespace pimoroni { - PicoGraphics_Pen3Bit::PicoGraphics_Pen3Bit(uint16_t width, uint16_t height, void *frame_buffer) - : PicoGraphics(width, height, frame_buffer) { + PicoGraphics_Pen3Bit::PicoGraphics_Pen3Bit(uint16_t width, uint16_t height, void *frame_buffer, uint16_t layers) + : PicoGraphics(width, height, layers, frame_buffer) { this->pen_type = PEN_3BIT; if(this->frame_buffer == nullptr) { this->frame_buffer = (void *)(new uint8_t[buffer_size(width, height)]); diff --git a/libraries/pico_graphics/pico_graphics_pen_inky7.cpp b/libraries/pico_graphics/pico_graphics_pen_inky7.cpp index 9a6ae5b0d..e41de871d 100644 --- a/libraries/pico_graphics/pico_graphics_pen_inky7.cpp +++ b/libraries/pico_graphics/pico_graphics_pen_inky7.cpp @@ -1,8 +1,8 @@ #include "pico_graphics.hpp" namespace pimoroni { - PicoGraphics_PenInky7::PicoGraphics_PenInky7(uint16_t width, uint16_t height, IDirectDisplayDriver &direct_display_driver) - : PicoGraphics(width, height, nullptr), + PicoGraphics_PenInky7::PicoGraphics_PenInky7(uint16_t width, uint16_t height, IDirectDisplayDriver &direct_display_driver, uint16_t layers) + : PicoGraphics(width, height, layers, nullptr), driver(direct_display_driver) { this->pen_type = PEN_INKY7; } diff --git a/libraries/pico_graphics/pico_graphics_pen_p4.cpp b/libraries/pico_graphics/pico_graphics_pen_p4.cpp index 68b3d62c2..bf5edc3fc 100644 --- a/libraries/pico_graphics/pico_graphics_pen_p4.cpp +++ b/libraries/pico_graphics/pico_graphics_pen_p4.cpp @@ -2,8 +2,8 @@ namespace pimoroni { - PicoGraphics_PenP4::PicoGraphics_PenP4(uint16_t width, uint16_t height, void *frame_buffer) - : PicoGraphics(width, height, frame_buffer) { + PicoGraphics_PenP4::PicoGraphics_PenP4(uint16_t width, uint16_t height, void *frame_buffer, uint16_t layers) + : PicoGraphics(width, height, layers, frame_buffer) { this->pen_type = PEN_P4; if(this->frame_buffer == nullptr) { this->frame_buffer = (void *)(new uint8_t[buffer_size(width, height)]); @@ -59,6 +59,7 @@ namespace pimoroni { // pointer to byte in framebuffer that contains this pixel uint8_t *buf = (uint8_t *)frame_buffer; + buf += this->layer_offset / 2; uint8_t *f = &buf[i / 2]; uint8_t o = (~i & 0b1) * 4; // bit offset within byte @@ -74,6 +75,7 @@ namespace pimoroni { // pointer to byte in framebuffer that contains this pixel uint8_t *buf = (uint8_t *)frame_buffer; + buf += this->layer_offset / 2; uint8_t *f = &buf[i / 2]; // doubled up color value, so the color is stored in both nibbles @@ -144,16 +146,39 @@ namespace pimoroni { uint8_t *src = (uint8_t *)frame_buffer; uint8_t o = 4; - frame_convert_rgb565(callback, [&]() { - uint8_t c = *src; - uint8_t b = (c >> o) & 0xf; // bit value shifted to position - - // Increment to next 4-bit entry - o ^= 4; - if (o != 0) ++src; - - return cache[b]; - }); + if(this->layers > 1) { + + uint offset = this->bounds.w * this->bounds.h / 2; + + frame_convert_rgb565(callback, [&]() { + uint8_t b = 0; + + // Iterate through layers in reverse order + // Return the first nonzero (not transparent) pixel + for(auto layer = this->layers; layer > 0; layer--) { + uint8_t c = *(src + offset * (layer - 1)); + b = (c >> o) & 0xf; // bit value shifted to position + if (b) break; + } + + // Increment to next 4-bit entry + o ^= 4; + if (o != 0) src++; + + return cache[b]; + }); + } else { + frame_convert_rgb565(callback, [&]() { + uint8_t c = *src; + uint8_t b = (c >> o) & 0xf; // bit value shifted to position + + // Increment to next 4-bit entry + o ^= 4; + if (o != 0) ++src; + + return cache[b]; + }); + } } } } diff --git a/libraries/pico_graphics/pico_graphics_pen_p8.cpp b/libraries/pico_graphics/pico_graphics_pen_p8.cpp index f7d0be73d..fd952690f 100644 --- a/libraries/pico_graphics/pico_graphics_pen_p8.cpp +++ b/libraries/pico_graphics/pico_graphics_pen_p8.cpp @@ -1,8 +1,8 @@ #include "pico_graphics.hpp" namespace pimoroni { - PicoGraphics_PenP8::PicoGraphics_PenP8(uint16_t width, uint16_t height, void *frame_buffer) - : PicoGraphics(width, height, frame_buffer) { + PicoGraphics_PenP8::PicoGraphics_PenP8(uint16_t width, uint16_t height, void *frame_buffer, uint16_t layers) + : PicoGraphics(width, height, layers, frame_buffer) { this->pen_type = PEN_P8; if(this->frame_buffer == nullptr) { this->frame_buffer = (void *)(new uint8_t[buffer_size(width, height)]); @@ -51,12 +51,14 @@ namespace pimoroni { } void PicoGraphics_PenP8::set_pixel(const Point &p) { uint8_t *buf = (uint8_t *)frame_buffer; + buf += this->layer_offset; buf[p.y * bounds.w + p.x] = color; } void PicoGraphics_PenP8::set_pixel_span(const Point &p, uint l) { // pointer to byte in framebuffer that contains this pixel uint8_t *buf = (uint8_t *)frame_buffer; + buf += this->layer_offset; buf = &buf[p.y * bounds.w + p.x]; while(l--) { @@ -103,26 +105,70 @@ namespace pimoroni { } void PicoGraphics_PenP8::frame_convert(PenType type, conversion_callback_func callback) { - if(type == PEN_RGB565) { - // Cache the RGB888 palette as RGB565 - RGB565 cache[palette_size]; - for(auto i = 0u; i < palette_size; i++) { - cache[i] = palette[i].to_rgb565(); - } + // Treat our void* frame_buffer as uint8_t + uint8_t *src = (uint8_t *)frame_buffer; + + if(layers > 1) { + // The size of a single layer + uint offset = this->bounds.w * this->bounds.h; + + if(type == PEN_RGB565) { + // Cache the RGB888 palette as RGB565 + RGB565 cache[palette_size]; + for(auto i = 0u; i < palette_size; i++) { + cache[i] = palette[i].to_rgb565(); + } + + frame_convert_rgb565(callback, [&]() { + // Check the *palette* index, rather than the colour + // Thus palette entry 0 is *always* transparent + uint8_t c = 0; + + // Iterate through layers in reverse order + // Return the first nonzero (not transparent) pixel + for(auto layer = this->layers; layer > 0; layer--) { + c = *(src + offset * (layer - 1)); + if (c) break; + } - // Treat our void* frame_buffer as uint8_t - uint8_t *src = (uint8_t *)frame_buffer; + src++; - frame_convert_rgb565(callback, [&]() { - return cache[*src++]; - }); - } else if (type == PEN_RGB888) { - // Treat our void* frame_buffer as uint8_t - uint8_t *src = (uint8_t *)frame_buffer; + return cache[c]; + }); + } else if (type == PEN_RGB888) { + frame_convert_rgb888(callback, [&]() { + // Check the *palette* index, rather than the colour + // Thus palette entry 0 is *always* transparent + uint8_t c = 0; - frame_convert_rgb888(callback, [&]() { - return palette[*src++].to_rgb888(); - }); + // Iterate through layers in reverse order + // Return the first nonzero (not transparent) pixel + for(auto layer = this->layers; layer > 0; layer--) { + c = *(src + offset * (layer - 1)); + if (c) break; + } + + src++; + + return palette[c].to_rgb888(); + }); + } + } else { + if(type == PEN_RGB565) { + // Cache the RGB888 palette as RGB565 + RGB565 cache[palette_size]; + for(auto i = 0u; i < palette_size; i++) { + cache[i] = palette[i].to_rgb565(); + } + + frame_convert_rgb565(callback, [&]() { + return cache[*src++]; + }); + } else if (type == PEN_RGB888) { + frame_convert_rgb888(callback, [&]() { + return palette[*src++].to_rgb888(); + }); + } } } } diff --git a/libraries/pico_graphics/pico_graphics_pen_rgb332.cpp b/libraries/pico_graphics/pico_graphics_pen_rgb332.cpp index 1bce808fc..3c8ca8521 100644 --- a/libraries/pico_graphics/pico_graphics_pen_rgb332.cpp +++ b/libraries/pico_graphics/pico_graphics_pen_rgb332.cpp @@ -2,11 +2,11 @@ #include namespace pimoroni { - PicoGraphics_PenRGB332::PicoGraphics_PenRGB332(uint16_t width, uint16_t height, void *frame_buffer) - : PicoGraphics(width, height, frame_buffer) { + PicoGraphics_PenRGB332::PicoGraphics_PenRGB332(uint16_t width, uint16_t height, void *frame_buffer, uint16_t layers) + : PicoGraphics(width, height, layers, frame_buffer) { this->pen_type = PEN_RGB332; if(this->frame_buffer == nullptr) { - this->frame_buffer = (void *)(new uint8_t[buffer_size(width, height)]); + this->frame_buffer = (void *)(new uint8_t[buffer_size(width, height) * layers]); } } void PicoGraphics_PenRGB332::set_pen(uint c) { @@ -23,12 +23,14 @@ namespace pimoroni { } void PicoGraphics_PenRGB332::set_pixel(const Point &p) { uint8_t *buf = (uint8_t *)frame_buffer; + buf += this->layer_offset; buf[p.y * bounds.w + p.x] = color; } void PicoGraphics_PenRGB332::set_pixel_span(const Point &p, uint l) { // pointer to byte in framebuffer that contains this pixel uint8_t *buf = (uint8_t *)frame_buffer; - buf = &buf[p.y * bounds.w + p.x]; + buf += this->layer_offset; + buf += p.y * bounds.w + p.x; while(l--) { *buf++ = color; @@ -38,6 +40,7 @@ namespace pimoroni { if(!bounds.contains(p)) return; uint8_t *buf = (uint8_t *)frame_buffer; + buf += this->layer_offset; RGB332 blended = RGB(buf[p.y * bounds.w + p.x]).blend(RGB(color), a).to_rgb332(); @@ -96,9 +99,29 @@ namespace pimoroni { // Treat our void* frame_buffer as uint8_t uint8_t *src = (uint8_t *)frame_buffer; - frame_convert_rgb565(callback, [&]() { - return rgb332_to_rgb565_lut[*src++]; - }); + if(this->layers > 1) { + // The size of a single layer + uint offset = this->bounds.w * this->bounds.h; + + frame_convert_rgb565(callback, [&]() { + uint8_t c = 0; + + // Iterate through layers in reverse order + // Return the first nonzero (not transparent) pixel + for(auto layer = this->layers; layer > 0; layer--) { + c = *(src + offset * (layer - 1)); + if (c) break; + } + + src++; + + return rgb332_to_rgb565_lut[c]; + }); + } else { + frame_convert_rgb565(callback, [&]() { + return rgb332_to_rgb565_lut[*src++]; + }); + } } } void PicoGraphics_PenRGB332::sprite(void* data, const Point &sprite, const Point &dest, const int scale, const int transparent) { diff --git a/libraries/pico_graphics/pico_graphics_pen_rgb565.cpp b/libraries/pico_graphics/pico_graphics_pen_rgb565.cpp index f17c6be47..ce0e42e92 100644 --- a/libraries/pico_graphics/pico_graphics_pen_rgb565.cpp +++ b/libraries/pico_graphics/pico_graphics_pen_rgb565.cpp @@ -1,8 +1,8 @@ #include "pico_graphics.hpp" namespace pimoroni { - PicoGraphics_PenRGB565::PicoGraphics_PenRGB565(uint16_t width, uint16_t height, void *frame_buffer) - : PicoGraphics(width, height, frame_buffer) { + PicoGraphics_PenRGB565::PicoGraphics_PenRGB565(uint16_t width, uint16_t height, void *frame_buffer, uint16_t layers) + : PicoGraphics(width, height, layers, frame_buffer) { this->pen_type = PEN_RGB565; if(this->frame_buffer == nullptr) { this->frame_buffer = (void *)(new uint8_t[buffer_size(width, height)]); @@ -23,15 +23,41 @@ namespace pimoroni { } void PicoGraphics_PenRGB565::set_pixel(const Point &p) { uint16_t *buf = (uint16_t *)frame_buffer; + // We can't use buffer_size because our pointer is uint16_t + buf += this->layer_offset; buf[p.y * bounds.w + p.x] = color; } void PicoGraphics_PenRGB565::set_pixel_span(const Point &p, uint l) { // pointer to byte in framebuffer that contains this pixel uint16_t *buf = (uint16_t *)frame_buffer; + // We can't use buffer_size because our pointer is uint16_t + buf += this->layer_offset; buf = &buf[p.y * bounds.w + p.x]; while(l--) { *buf++ = color; } } + void PicoGraphics_PenRGB565::frame_convert(PenType type, conversion_callback_func callback) { + if(type == PEN_RGB565) { + // Treat our void* frame_buffer as uint8_t + uint16_t *src = (uint16_t *)frame_buffer; + + if(layers > 1) { + // Assume only two layers for now + // We can't use buffer_size because our pointer is uint16_t + uint16_t *src_layer2 = src + this->bounds.w * this->bounds.h; + + frame_convert_rgb565(callback, [&]() { + RGB565 c1 = *src++; + RGB565 c2 = *src_layer2++; + return c2 ? c2 : c1; + }); + } else { + frame_convert_rgb565(callback, [&]() { + return *src++; + }); + } + } + } } \ No newline at end of file diff --git a/libraries/pico_graphics/pico_graphics_pen_rgb888.cpp b/libraries/pico_graphics/pico_graphics_pen_rgb888.cpp index 6cc9d2adf..0b145239b 100644 --- a/libraries/pico_graphics/pico_graphics_pen_rgb888.cpp +++ b/libraries/pico_graphics/pico_graphics_pen_rgb888.cpp @@ -1,8 +1,8 @@ #include "pico_graphics.hpp" namespace pimoroni { - PicoGraphics_PenRGB888::PicoGraphics_PenRGB888(uint16_t width, uint16_t height, void *frame_buffer) - : PicoGraphics(width, height, frame_buffer) { + PicoGraphics_PenRGB888::PicoGraphics_PenRGB888(uint16_t width, uint16_t height, void *frame_buffer, uint16_t layers) + : PicoGraphics(width, height, layers, frame_buffer) { this->pen_type = PEN_RGB888; if(this->frame_buffer == nullptr) { this->frame_buffer = (void *)(new uint8_t[buffer_size(width, height)]); @@ -23,11 +23,13 @@ namespace pimoroni { } void PicoGraphics_PenRGB888::set_pixel(const Point &p) { uint32_t *buf = (uint32_t *)frame_buffer; + buf += this->layer_offset; buf[p.y * bounds.w + p.x] = color; } void PicoGraphics_PenRGB888::set_pixel_span(const Point &p, uint l) { // pointer to byte in framebuffer that contains this pixel uint32_t *buf = (uint32_t *)frame_buffer; + buf += this->layer_offset; buf = &buf[p.y * bounds.w + p.x]; while(l--) { diff --git a/micropython/board/PICO_W_ENVIRO/pico_sdk.patch b/micropython/board/PICO_W_ENVIRO/pico_sdk.off.patch similarity index 100% rename from micropython/board/PICO_W_ENVIRO/pico_sdk.patch rename to micropython/board/PICO_W_ENVIRO/pico_sdk.off.patch diff --git a/micropython/board/PICO_W_INKY/pico_sdk.patch b/micropython/board/PICO_W_INKY/pico_sdk.off.patch similarity index 100% rename from micropython/board/PICO_W_INKY/pico_sdk.patch rename to micropython/board/PICO_W_INKY/pico_sdk.off.patch diff --git a/micropython/modules/picographics/picographics.c b/micropython/modules/picographics/picographics.c index b1aeec19e..ad6d0dc57 100644 --- a/micropython/modules/picographics/picographics.c +++ b/micropython/modules/picographics/picographics.c @@ -24,6 +24,9 @@ MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(ModPicoGraphics_create_pen_obj, 4, 4, ModPic MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(ModPicoGraphics_create_pen_hsv_obj, 4, 4, ModPicoGraphics_create_pen_hsv); MP_DEFINE_CONST_FUN_OBJ_2(ModPicoGraphics_set_thickness_obj, ModPicoGraphics_set_thickness); +// Layers +MP_DEFINE_CONST_FUN_OBJ_2(ModPicoGraphics_set_layer_obj, ModPicoGraphics_set_layer); + // Primitives MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(ModPicoGraphics_set_clip_obj, 5, 5, ModPicoGraphics_set_clip); MP_DEFINE_CONST_FUN_OBJ_1(ModPicoGraphics_remove_clip_obj, ModPicoGraphics_remove_clip); @@ -60,6 +63,8 @@ static const mp_rom_map_elem_t ModPicoGraphics_locals_dict_table[] = { { MP_ROM_QSTR(MP_QSTR_set_thickness), MP_ROM_PTR(&ModPicoGraphics_set_thickness_obj) }, { MP_ROM_QSTR(MP_QSTR_clear), MP_ROM_PTR(&ModPicoGraphics_clear_obj) }, + { MP_ROM_QSTR(MP_QSTR_set_layer), MP_ROM_PTR(&ModPicoGraphics_set_layer_obj) }, + { MP_ROM_QSTR(MP_QSTR_update), MP_ROM_PTR(&ModPicoGraphics_update_obj) }, { MP_ROM_QSTR(MP_QSTR_partial_update), MP_ROM_PTR(&ModPicoGraphics_partial_update_obj) }, { MP_ROM_QSTR(MP_QSTR_set_update_speed), MP_ROM_PTR(&ModPicoGraphics_set_update_speed_obj) }, @@ -157,6 +162,7 @@ static const mp_map_elem_t picographics_globals_table[] = { { MP_ROM_QSTR(MP_QSTR_DISPLAY_UNICORN_PACK), MP_ROM_INT(DISPLAY_UNICORN_PACK) }, { MP_ROM_QSTR(MP_QSTR_DISPLAY_SCROLL_PACK), MP_ROM_INT(DISPLAY_SCROLL_PACK) }, { MP_ROM_QSTR(MP_QSTR_DISPLAY_PICO_W_EXPLORER), MP_ROM_INT(DISPLAY_PICO_W_EXPLORER) }, + { MP_ROM_QSTR(MP_QSTR_DISPLAY_EXPLORER), MP_ROM_INT(DISPLAY_EXPLORER) }, { MP_ROM_QSTR(MP_QSTR_PEN_1BIT), MP_ROM_INT(PEN_1BIT) }, { MP_ROM_QSTR(MP_QSTR_PEN_P4), MP_ROM_INT(PEN_P4) }, diff --git a/micropython/modules/picographics/picographics.cpp b/micropython/modules/picographics/picographics.cpp index 4fe1e2d04..8ac300cf0 100644 --- a/micropython/modules/picographics/picographics.cpp +++ b/micropython/modules/picographics/picographics.cpp @@ -44,6 +44,13 @@ typedef struct _ModPicoGraphics_obj_t { bool get_display_settings(PicoGraphicsDisplay display, int &width, int &height, int &rotate, int &pen_type, PicoGraphicsBusType &bus_type) { switch(display) { + case DISPLAY_EXPLORER: + width = 320; + height = 240; + bus_type = BUS_PARALLEL; + if(rotate == -1) rotate = (int)Rotation::ROTATE_0; + if(pen_type == -1) pen_type = PEN_RGB565; + break; case DISPLAY_PICO_DISPLAY: width = 240; height = 135; @@ -247,24 +254,24 @@ bool get_display_settings(PicoGraphicsDisplay display, int &width, int &height, return true; } -size_t get_required_buffer_size(PicoGraphicsPenType pen_type, uint width, uint height) { +size_t get_required_buffer_size(PicoGraphicsPenType pen_type, uint width, uint height, uint layers) { switch(pen_type) { case PEN_1BIT: - return PicoGraphics_Pen1Bit::buffer_size(width, height); + return PicoGraphics_Pen1Bit::buffer_size(width, height) * layers; case PEN_3BIT: - return PicoGraphics_Pen3Bit::buffer_size(width, height); + return PicoGraphics_Pen3Bit::buffer_size(width, height) * layers; case PEN_P4: - return PicoGraphics_PenP4::buffer_size(width, height); + return PicoGraphics_PenP4::buffer_size(width, height) * layers; case PEN_P8: - return PicoGraphics_PenP8::buffer_size(width, height); + return PicoGraphics_PenP8::buffer_size(width, height) * layers; case PEN_RGB332: - return PicoGraphics_PenRGB332::buffer_size(width, height); + return PicoGraphics_PenRGB332::buffer_size(width, height) * layers; case PEN_RGB565: - return PicoGraphics_PenRGB565::buffer_size(width, height); + return PicoGraphics_PenRGB565::buffer_size(width, height) * layers; case PEN_RGB888: - return PicoGraphics_PenRGB888::buffer_size(width, height); + return PicoGraphics_PenRGB888::buffer_size(width, height) * layers; case PEN_INKY7: - return PicoGraphics_PenInky7::buffer_size(width, height); + return PicoGraphics_PenInky7::buffer_size(width, height) * layers; default: return 0; } @@ -273,7 +280,7 @@ size_t get_required_buffer_size(PicoGraphicsPenType pen_type, uint width, uint h mp_obj_t ModPicoGraphics_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) { ModPicoGraphics_obj_t *self = nullptr; - enum { ARG_display, ARG_rotate, ARG_bus, ARG_buffer, ARG_pen_type, ARG_extra_pins, ARG_i2c_address }; + enum { ARG_display, ARG_rotate, ARG_bus, ARG_buffer, ARG_pen_type, ARG_extra_pins, ARG_i2c_address, ARG_layers }; static const mp_arg_t allowed_args[] = { { MP_QSTR_display, MP_ARG_INT | MP_ARG_REQUIRED }, { MP_QSTR_rotate, MP_ARG_INT, { .u_int = -1 } }, @@ -282,6 +289,7 @@ mp_obj_t ModPicoGraphics_make_new(const mp_obj_type_t *type, size_t n_args, size { MP_QSTR_pen_type, MP_ARG_INT, { .u_int = -1 } }, { MP_QSTR_extra_pins, MP_ARG_OBJ, { .u_obj = mp_const_none } }, { MP_QSTR_i2c_address, MP_ARG_INT, { .u_int = -1 } }, + { MP_QSTR_layers, MP_ARG_INT, { .u_int = 1 } }, }; // Parse args. @@ -297,6 +305,7 @@ mp_obj_t ModPicoGraphics_make_new(const mp_obj_type_t *type, size_t n_args, size int height = 0; int pen_type = args[ARG_pen_type].u_int; int rotate = args[ARG_rotate].u_int; + int layers = args[ARG_layers].u_int; PicoGraphicsBusType bus_type = BUS_SPI; if(!get_display_settings(display, width, height, rotate, pen_type, bus_type)) mp_raise_ValueError("Unsupported display!"); if(rotate == -1) rotate = (int)Rotation::ROTATE_0; @@ -335,6 +344,10 @@ mp_obj_t ModPicoGraphics_make_new(const mp_obj_type_t *type, size_t n_args, size } else if (display == DISPLAY_PICO_W_EXPLORER) { spi_bus = {PIMORONI_SPI_DEFAULT_INSTANCE, 17, SPI_DEFAULT_SCK, SPI_DEFAULT_MOSI, PIN_UNUSED, SPI_DEFAULT_MISO, 9}; } + } else if (bus_type == BUS_PARALLEL) { + if (display == DISPLAY_EXPLORER) { + parallel_bus = {27, 28, 30, 31, 32, 26}; + } } } @@ -349,7 +362,8 @@ mp_obj_t ModPicoGraphics_make_new(const mp_obj_type_t *type, size_t n_args, size // TODO grab BUSY and RESET from ARG_extra_pins self->display = m_new_class(Inky73, width, height, (Rotation)rotate, spi_bus); - } else if (display == DISPLAY_TUFTY_2040) { + } else if (display == DISPLAY_TUFTY_2040 + || display == DISPLAY_EXPLORER) { self->display = m_new_class(ST7789, width, height, (Rotation)rotate, parallel_bus); } else if (display == DISPLAY_LCD_160X80) { @@ -383,7 +397,7 @@ mp_obj_t ModPicoGraphics_make_new(const mp_obj_type_t *type, size_t n_args, size } // Create or fetch buffer - size_t required_size = get_required_buffer_size((PicoGraphicsPenType)pen_type, width, height); + size_t required_size = get_required_buffer_size((PicoGraphicsPenType)pen_type, width, height, layers); if(required_size == 0) mp_raise_ValueError("Unsupported pen type!"); if(pen_type == PEN_INKY7) { @@ -406,31 +420,31 @@ mp_obj_t ModPicoGraphics_make_new(const mp_obj_type_t *type, size_t n_args, size switch((PicoGraphicsPenType)pen_type) { case PEN_1BIT: if (display == DISPLAY_INKY_PACK) { - self->graphics = m_new_class(PicoGraphics_Pen1BitY, self->display->width, self->display->height, self->buffer); + self->graphics = m_new_class(PicoGraphics_Pen1BitY, self->display->width, self->display->height, self->buffer, layers); } else { - self->graphics = m_new_class(PicoGraphics_Pen1Bit, self->display->width, self->display->height, self->buffer); + self->graphics = m_new_class(PicoGraphics_Pen1Bit, self->display->width, self->display->height, self->buffer, layers); } break; case PEN_3BIT: - self->graphics = m_new_class(PicoGraphics_Pen3Bit, self->display->width, self->display->height, self->buffer); + self->graphics = m_new_class(PicoGraphics_Pen3Bit, self->display->width, self->display->height, self->buffer, layers); break; case PEN_P4: - self->graphics = m_new_class(PicoGraphics_PenP4, self->display->width, self->display->height, self->buffer); + self->graphics = m_new_class(PicoGraphics_PenP4, self->display->width, self->display->height, self->buffer, layers); break; case PEN_P8: - self->graphics = m_new_class(PicoGraphics_PenP8, self->display->width, self->display->height, self->buffer); + self->graphics = m_new_class(PicoGraphics_PenP8, self->display->width, self->display->height, self->buffer, layers); break; case PEN_RGB332: - self->graphics = m_new_class(PicoGraphics_PenRGB332, self->display->width, self->display->height, self->buffer); + self->graphics = m_new_class(PicoGraphics_PenRGB332, self->display->width, self->display->height, self->buffer, layers); break; case PEN_RGB565: - self->graphics = m_new_class(PicoGraphics_PenRGB565, self->display->width, self->display->height, self->buffer); + self->graphics = m_new_class(PicoGraphics_PenRGB565, self->display->width, self->display->height, self->buffer, layers); break; case PEN_RGB888: - self->graphics = m_new_class(PicoGraphics_PenRGB888, self->display->width, self->display->height, self->buffer); + self->graphics = m_new_class(PicoGraphics_PenRGB888, self->display->width, self->display->height, self->buffer, layers); break; case PEN_INKY7: - self->graphics = m_new_class(PicoGraphics_PenInky7, self->display->width, self->display->height, *(IDirectDisplayDriver *)self->buffer); + self->graphics = m_new_class(PicoGraphics_PenInky7, self->display->width, self->display->height, *(IDirectDisplayDriver *)self->buffer, layers); break; default: break; @@ -441,8 +455,15 @@ mp_obj_t ModPicoGraphics_make_new(const mp_obj_type_t *type, size_t n_args, size self->spritedata = nullptr; // Clear the buffer + self->graphics->set_layer(0); self->graphics->set_pen(0); self->graphics->clear(); + if(layers > 1) { + self->graphics->set_layer(1); + self->graphics->set_pen(0); + self->graphics->clear(); + self->graphics->set_layer(0); + } // Update the LCD from the graphics library if (display != DISPLAY_INKY_FRAME && display != DISPLAY_INKY_FRAME_4 && display != DISPLAY_INKY_PACK && display != DISPLAY_INKY_FRAME_7) { @@ -465,7 +486,7 @@ mp_obj_t ModPicoGraphics_set_spritesheet(mp_obj_t self_in, mp_obj_t spritedata) mp_buffer_info_t bufinfo; mp_get_buffer_raise(spritedata, &bufinfo, MP_BUFFER_RW); - int required_size = get_required_buffer_size((PicoGraphicsPenType)self->graphics->pen_type, 128, 128); + int required_size = get_required_buffer_size((PicoGraphicsPenType)self->graphics->pen_type, 128, 128, 1); if(bufinfo.len != (size_t)(required_size)) { mp_raise_ValueError("Spritesheet the wrong size!"); @@ -550,7 +571,7 @@ mp_int_t ModPicoGraphics_get_framebuffer(mp_obj_t self_in, mp_buffer_info_t *buf mp_raise_ValueError("No local framebuffer."); } bufinfo->buf = self->graphics->frame_buffer; - bufinfo->len = get_required_buffer_size((PicoGraphicsPenType)self->graphics->pen_type, self->graphics->bounds.w, self->graphics->bounds.h); + bufinfo->len = get_required_buffer_size((PicoGraphicsPenType)self->graphics->pen_type, self->graphics->bounds.w, self->graphics->bounds.h, 1); bufinfo->typecode = 'B'; return 0; } @@ -591,7 +612,7 @@ mp_obj_t ModPicoGraphics_get_required_buffer_size(mp_obj_t display_in, mp_obj_t int pen_type = mp_obj_get_int(pen_type_in); PicoGraphicsBusType bus_type = BUS_SPI; if(!get_display_settings(display, width, height, rotation, pen_type, bus_type)) mp_raise_ValueError("Unsupported display!"); - size_t required_size = get_required_buffer_size((PicoGraphicsPenType)pen_type, width, height); + size_t required_size = get_required_buffer_size((PicoGraphicsPenType)pen_type, width, height, 1); if(required_size == 0) mp_raise_ValueError("Unsupported pen type!"); return mp_obj_new_int(required_size); @@ -743,6 +764,14 @@ mp_obj_t ModPicoGraphics_set_pen(mp_obj_t self_in, mp_obj_t pen) { return mp_const_none; } +mp_obj_t ModPicoGraphics_set_layer(mp_obj_t self_in, mp_obj_t layer) { + ModPicoGraphics_obj_t *self = MP_OBJ_TO_PTR2(self_in, ModPicoGraphics_obj_t); + + self->graphics->set_layer(mp_obj_get_int(layer)); + + return mp_const_none; +} + mp_obj_t ModPicoGraphics_reset_pen(mp_obj_t self_in, mp_obj_t pen) { ModPicoGraphics_obj_t *self = MP_OBJ_TO_PTR2(self_in, ModPicoGraphics_obj_t); diff --git a/micropython/modules/picographics/picographics.h b/micropython/modules/picographics/picographics.h index 1812a875f..1238354d9 100644 --- a/micropython/modules/picographics/picographics.h +++ b/micropython/modules/picographics/picographics.h @@ -30,7 +30,8 @@ enum PicoGraphicsDisplay { DISPLAY_STELLAR_UNICORN, DISPLAY_UNICORN_PACK, DISPLAY_SCROLL_PACK, - DISPLAY_PICO_W_EXPLORER + DISPLAY_PICO_W_EXPLORER, + DISPLAY_EXPLORER }; enum PicoGraphicsPenType { @@ -76,6 +77,9 @@ extern mp_obj_t ModPicoGraphics_reset_pen(mp_obj_t self_in, mp_obj_t pen); extern mp_obj_t ModPicoGraphics_set_palette(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args); extern mp_obj_t ModPicoGraphics_hsv_to_rgb(size_t n_args, const mp_obj_t *args); +// Layers +extern mp_obj_t ModPicoGraphics_set_layer(mp_obj_t self_in, mp_obj_t layer); + // Pen extern mp_obj_t ModPicoGraphics_set_pen(mp_obj_t self_in, mp_obj_t pen); extern mp_obj_t ModPicoGraphics_create_pen(size_t n_args, const mp_obj_t *args); diff --git a/micropython/modules/wakeup/wakeup.c b/micropython/modules/wakeup/wakeup.c index 69f2422c3..0b9d523e8 100644 --- a/micropython/modules/wakeup/wakeup.c +++ b/micropython/modules/wakeup/wakeup.c @@ -1,4 +1,25 @@ #include "wakeup.h" +#include "hardware/gpio.h" +#include "wakeup.config.hpp" +#include "pico/runtime_init.h" + + +uint32_t runtime_wakeup_gpio_state = 0; + +// Pins to toggle on wakeup +#ifndef PICO_WAKEUP_PIN_MASK +#define PICO_WAKEUP_PIN_MASK ((0b1 << 2) | (0b1 << 6)) +#endif + +// Direction +#ifndef PICO_WAKEUP_PIN_DIR +#define PICO_WAKEUP_PIN_DIR ((0b1 << 2) | (0b1 << 6)) +#endif + +// Value +#ifndef PICO_WAKEUP_PIN_VALUE +#define PICO_WAKEUP_PIN_VALUE ((0b1 << 2) | (0b1 << 6)) +#endif static MP_DEFINE_CONST_FUN_OBJ_0(Wakeup_get_gpio_state_obj, Wakeup_get_gpio_state); static MP_DEFINE_CONST_FUN_OBJ_0(Wakeup_reset_gpio_state_obj, Wakeup_reset_gpio_state); @@ -23,4 +44,17 @@ const mp_obj_module_t wakeup_user_cmodule = { MP_REGISTER_MODULE(MP_QSTR_wakeup, wakeup_user_cmodule, MODULE_WAKEUP_ENABLED); #else MP_REGISTER_MODULE(MP_QSTR_wakeup, wakeup_user_cmodule); -#endif \ No newline at end of file +#endif + +void runtime_init_latch(void) { + runtime_wakeup_gpio_state = gpio_get_all(); + + gpio_init_mask(PICO_WAKEUP_PIN_MASK); + gpio_set_dir_masked(PICO_WAKEUP_PIN_MASK, PICO_WAKEUP_PIN_DIR); + gpio_put_masked(PICO_WAKEUP_PIN_MASK, PICO_WAKEUP_PIN_VALUE); +}; + +// After runtime_init_early_resets, PICO_RUNTIME_INIT_EARLY_RESETS ? +PICO_RUNTIME_INIT_FUNC_HW(runtime_init_latch, "00101"); +// Too early? +// PICO_RUNTIME_INIT_FUNC_HW(runtime_init_latch, PICO_RUNTIME_INIT_EARLIEST);