Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
47 commits
Select commit Hold shift + click to select a range
e50bb06
st7701: Decouple the driver from PicoGraphics.
Gadgetoid Aug 26, 2026
5bbd62a
build: Swap PicoGraphics and PicoVector for picovector-micropython.
Gadgetoid Aug 26, 2026
feb3262
presto: Rasterise into an RGBA8888 surface.
Gadgetoid Aug 26, 2026
d47e0ab
presto.py: Expose the PicoVector drawing surface as display.
Gadgetoid Aug 26, 2026
0ff6ad0
Add Badgeware's wifi and fetch libraries.
Gadgetoid Aug 26, 2026
9601e2e
examples: Port hello and balls_demo to the PicoVector API.
Gadgetoid Aug 26, 2026
8aead6d
CI: Build against picovector-micropython and the badgeware MicroPython.
Gadgetoid Aug 26, 2026
d142c24
examples: Port the launcher to the PicoVector API.
Gadgetoid Aug 26, 2026
72ff592
examples: Port touchscreen_dots to the PicoVector API.
Gadgetoid Aug 26, 2026
1da1976
examples: Port buzzer to the PicoVector API.
Gadgetoid Aug 26, 2026
d987cd1
examples: Port auto_backlight to the PicoVector API.
Gadgetoid Aug 26, 2026
33ef6dd
examples: Port backlight_ball to the PicoVector API.
Gadgetoid Aug 26, 2026
032cecd
examples: Port touch_buttons to the PicoVector API.
Gadgetoid Aug 26, 2026
dc844c5
examples: Port backlight_slider to the PicoVector API.
Gadgetoid Aug 26, 2026
4dc0153
examples: Port badapple to the PicoVector API.
Gadgetoid Aug 26, 2026
39ccace
examples: Port backlight_images to the PicoVector API.
Gadgetoid Aug 26, 2026
dff150a
examples: Port cubes to the PicoVector API.
Gadgetoid Aug 26, 2026
d66e778
examples: Port sd_image to the PicoVector API.
Gadgetoid Aug 26, 2026
fb1013e
examples: Port hampws_light to the PicoVector API.
Gadgetoid Aug 26, 2026
e02b0f0
examples: Port fire_effect to the PicoVector API.
Gadgetoid Aug 26, 2026
ecf362d
examples: Port word_clock to the PicoVector API.
Gadgetoid Aug 26, 2026
27c5db2
examples: Port world_clock_fr to the PicoVector API.
Gadgetoid Aug 26, 2026
0058844
examples: Port touchscreen_dots_vector to the PicoVector API.
Gadgetoid Aug 26, 2026
f82d95f
examples: Port random_maze to the PicoVector API.
Gadgetoid Aug 26, 2026
9bac6df
examples: Port stop_watch to the PicoVector API.
Gadgetoid Aug 26, 2026
9850a3b
examples: Port tomato to the PicoVector API.
Gadgetoid Aug 26, 2026
a7e3204
examples: Port cheerlights_bulb to the PicoVector API.
Gadgetoid Aug 26, 2026
329f02f
examples: Port sensor-stick-temperature to the PicoVector API.
Gadgetoid Aug 26, 2026
0ffaad6
examples: Port indoor-outdoor-temp to the PicoVector API.
Gadgetoid Aug 26, 2026
21bc2fe
examples: Port image_gallery to the PicoVector API.
Gadgetoid Aug 26, 2026
c4d3909
examples: Port word_time to the PicoVector API.
Gadgetoid Aug 26, 2026
d2495be
examples: Update co2 for the SCD41 class API.
Gadgetoid Aug 26, 2026
ad32278
examples: Port co2 to the PicoVector API.
Gadgetoid Aug 26, 2026
52a568a
examples: Port multi_player to the PicoVector API.
Gadgetoid Aug 26, 2026
8731efd
examples: Port splash to the PicoVector API.
Gadgetoid Aug 26, 2026
c5f1b58
examples: Port attitude_indicator to the PicoVector API.
Gadgetoid Aug 26, 2026
6754f80
examples: Port agile_pricing_display to the PicoVector API.
Gadgetoid Aug 26, 2026
762a11f
examples: Port vector_clock_full to the PicoVector API.
Gadgetoid Aug 26, 2026
e92a661
examples: Port awesome_game to the PicoVector API.
Gadgetoid Aug 26, 2026
f33c62b
examples: Restore antialiasing, and fix launcher text placement.
Gadgetoid Aug 26, 2026
6036a92
st7701: Scan full res out of PSRAM through an SRAM line cache.
Gadgetoid Aug 26, 2026
87b8441
examples: Give the launcher tiles a gradient fill.
Gadgetoid Aug 26, 2026
d0ca0c9
examples: Gradient backdrop and snap-to-centre for the launcher.
Gadgetoid Aug 26, 2026
852fa34
Revert "st7701: Scan full res out of PSRAM through an SRAM line cache."
Gadgetoid Aug 26, 2026
1d4922a
Drop full res: PicoVector cannot drive the panel at 480x480.
Gadgetoid Aug 26, 2026
f0c1847
examples: Antialias cubes and centre its title.
Gadgetoid Aug 26, 2026
0532e69
usermod: Drop redundant qrcode module.
Gadgetoid Aug 31, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion boards/presto/manifest.txt
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
*.af
*.png
*.16bpp
main.py
attitude_indicator.py
awesome_game.py
Expand Down
14 changes: 4 additions & 10 deletions boards/usermod-common.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -21,16 +21,10 @@ include(modules/c/presto/micropython)
include(pimoroni_i2c/micropython)
include(pimoroni_bus/micropython)

# Pico Graphics Essential
include(hershey_fonts/micropython)
include(bitmap_fonts/micropython)
include(picographics/micropython)

# Pico Graphics Extra
include(pngdec/micropython)
include(jpegdec/micropython)
include(picovector/micropython)
include(qrcode/micropython/micropython)
# PicoVector & MicroPython bindings.
# Core1 runs the ST7701 scanout, so the rasteriser stays on core0.
set(PV_DUAL_CORE OFF)
find_package(PICOVECTOR_MICROPYTHON CONFIG REQUIRED)

# Sensors & Breakouts
include(micropython-common-breakouts)
Expand Down
15 changes: 14 additions & 1 deletion ci/micropython.sh
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
export TERM=${TERM:="xterm-256color"}

MICROPYTHON_FLAVOUR="pimoroni"
MICROPYTHON_VERSION="feature/presto-wireless-august-2026"
MICROPYTHON_VERSION="feature/presto-badgeware"

PIMORONI_PICO_FLAVOUR="pimoroni"
PIMORONI_PICO_VERSION="patch/micropython-1.29.0"

PIMORONI_PICOVECTOR_VERSION="main"

PY_DECL_VERSION="v0.0.3"
DIR2UF2_VERSION="v0.0.9"

Expand All @@ -31,6 +33,15 @@ function ci_pimoroni_pico_clone {
cd "$CI_BUILD_ROOT"
}

function ci_picovector_clone {
log_inform "Using Pimoroni PicoVector pimoroni/$PIMORONI_PICOVECTOR_VERSION"
git clone https://github.com/pimoroni/picovector-micropython "$CI_BUILD_ROOT/picovector-micropython"
cd "$CI_BUILD_ROOT/picovector-micropython" || return 1
git checkout $PIMORONI_PICOVECTOR_VERSION
git submodule update --init
cd "$CI_BUILD_ROOT"
}

function ci_micropython_clone {
log_inform "Using MicroPython $MICROPYTHON_FLAVOUR/$MICROPYTHON_VERSION"
git clone https://github.com/$MICROPYTHON_FLAVOUR/micropython "$CI_BUILD_ROOT/micropython"
Expand Down Expand Up @@ -69,6 +80,7 @@ function ci_prepare_all {
ci_tools_clone
ci_micropython_clone
ci_pimoroni_pico_clone
ci_picovector_clone
ci_micropython_build_mpy_cross
}

Expand Down Expand Up @@ -98,6 +110,7 @@ function ci_cmake_configure {
-DPICO_NO_COPRO_DIS=1 \
-DPICOTOOL_FETCH_FROM_GIT_PATH="$TOOLS_DIR/picotool" \
-DPIMORONI_PICO_PATH="$CI_BUILD_ROOT/pimoroni-pico" \
-DPICOVECTOR_MICROPYTHON_DIR="$CI_BUILD_ROOT/picovector-micropython" \
-DPIMORONI_TOOLS_DIR="$TOOLS_DIR" \
-DUSER_C_MODULES="$MICROPY_BOARD_DIR/usermodules.cmake" \
-DMICROPY_BOARD_DIR="$MICROPY_BOARD_DIR" \
Expand Down
129 changes: 30 additions & 99 deletions drivers/st7701/st7701.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -234,7 +234,9 @@ void __not_in_flash_func(ST7701::start_frame_xfer)()

ST7701::ST7701(uint16_t width, uint16_t height, Rotation rotation, SPIPins control_pins, uint16_t* framebuffer, uint32_t* palette,
uint d0, uint hsync, uint vsync, uint lcd_de, uint lcd_dot_clk) :
DisplayDriver(width, height, rotation),
width(width),
height(height),
rotation(rotation),
spi(control_pins.spi),
spi_cs(control_pins.cs), spi_sck(control_pins.sck), spi_dat(control_pins.mosi), lcd_bl(control_pins.bl),
d0(d0), hsync(hsync), vsync(vsync), lcd_de(lcd_de), lcd_dot_clk(lcd_dot_clk),
Expand Down Expand Up @@ -633,97 +635,39 @@ void __not_in_flash_func(ST7701::start_frame_xfer)()
}

///
void ST7701::update(PicoGraphics *graphics) {
if(graphics->pen_type == PicoGraphics::PEN_RGB565 && !palette) { // Display buffer is screen native
if (graphics->frame_buffer == framebuffer) {
// Nothing to do
return;
// Convert an RGBA8888 source into the RGB565 scanout buffer. The copy stays
// behind the line currently being scanned out, so a frame is never torn.
void __no_inline_not_in_flash_func(ST7701::update)(const uint32_t *source) {
const uint32_t *src_ptr = source;
uint16_t *dst_ptr = framebuffer;
uint16_t *end_ptr = dst_ptr + width * height;
volatile uintptr_t *next_addr_ptr = (volatile uintptr_t *)&next_line_addr;

while (dst_ptr != end_ptr) {
uint16_t *next_addr = (uint16_t *)*next_addr_ptr;
if (!next_addr || next_addr == framebuffer) {
next_addr = framebuffer + width * height;
}

// Take care to copy while not passing the point in the frame buffer
// that is currently being scanned out to the screen. This prevents tearing.
uint16_t* src_ptr = (uint16_t*)graphics->frame_buffer;
uint16_t* src_ptr2 = (uint16_t*)graphics->frame_buffer + width * height;
uint16_t* dst_ptr = framebuffer;
uint16_t* end_ptr = dst_ptr + width * height;
volatile uintptr_t* next_addr_ptr = (volatile uintptr_t*)&next_line_addr;

while (dst_ptr != end_ptr) {
uint16_t* next_addr = (uint16_t*)*next_addr_ptr;
if (!next_addr || next_addr == framebuffer) {
next_addr = framebuffer + width * height;
}

next_addr -= width;
if (next_addr < dst_ptr) {
// Ahead of the scanout, race the beam.
next_addr = end_ptr;
}
if (dst_ptr < next_addr) {
if (graphics->layers == 1) {
// Copy up to the current line being scanned out
int len = next_addr - dst_ptr;
memcpy(dst_ptr, src_ptr, len * sizeof(uint16_t));
dst_ptr += len;
src_ptr += len;
} else {
// Assume 2 layers
while (dst_ptr < next_addr) {
*dst_ptr++ = *src_ptr2 ? *src_ptr2 : *src_ptr;
++src_ptr2;
++src_ptr;
}
}
}
}
} else if (graphics->pen_type == PicoGraphics::PEN_P8 && palette) {
wait_for_vsync();
PicoGraphics_PenP8* pen8 = static_cast<PicoGraphics_PenP8*>(graphics);
RGB* palette = pen8->get_palette();
for (int i = 0; i < 256; ++i) {
set_palette_colour(i, palette[i]);
next_addr -= width;
if (next_addr < dst_ptr) {
// Ahead of the scanout, race the beam.
next_addr = end_ptr;
}
if (graphics->layers == 1) {
memcpy(framebuffer, graphics->frame_buffer, width * height);
while (dst_ptr < next_addr) {
const uint32_t src = *src_ptr++;
*dst_ptr++ = __builtin_bswap16(((src & 0xf8) << 8) | ((src & 0xfc00) >> 5) | ((src & 0xf80000) >> 19));
}
else {
uint8_t* dst = (uint8_t*)framebuffer;
const uint8_t* end = dst + width * height;
uint8_t* src = (uint8_t*)graphics->frame_buffer;
const size_t layer_offset = width * height;
const int top_layer_idx = graphics->layers - 1;

while (dst != end) {
uint8_t colour = 0;
for (int layer = top_layer_idx; layer >= 0; --layer) {
colour = *(src + layer * layer_offset);
if (colour) break;
}
*dst++ = colour;
++src;
}
}
} else {
uint8_t* frame_ptr = (uint8_t*)framebuffer;
graphics->frame_convert(PicoGraphics::PEN_RGB565, [this, &frame_ptr](void *data, size_t length) {
if (length > 0) {
memcpy(frame_ptr, data, length);
frame_ptr += length;
}
});
}
}

void ST7701::partial_update(PicoGraphics *graphics, Rect region) {
if (graphics->pen_type == PicoGraphics::PEN_RGB565 && !palette && graphics->layers == 1) { // Display buffer is screen native
for (int y = region.y; y < region.y + region.h; ++y) {
memcpy(&framebuffer[y * width + region.x], (uint16_t*)graphics->frame_buffer + y * width + region.x, region.w * sizeof(uint16_t));
}
}
else if (graphics->pen_type == PicoGraphics::PEN_P8 && palette && graphics->layers == 1) {
uint8_t* fb8 = (uint8_t*)framebuffer;
for (int y = region.y; y < region.y + region.h; ++y) {
memcpy(&fb8[y * width + region.x], (uint8_t*)graphics->frame_buffer + y * width + region.x, region.w * sizeof(uint8_t));
void ST7701::partial_update(const uint32_t *source, int x, int y, int w, int h) {
for (int row = y; row < y + h; ++row) {
const uint32_t *src_ptr = source + row * width + x;
uint16_t *dst_ptr = &framebuffer[row * width + x];
for (int col = 0; col < w; ++col) {
const uint32_t src = *src_ptr++;
*dst_ptr++ = __builtin_bswap16(((src & 0xf8) << 8) | ((src & 0xfc00) >> 5) | ((src & 0xf80000) >> 19));
}
}
}
Expand All @@ -737,7 +681,7 @@ void __not_in_flash_func(ST7701::start_frame_xfer)()
pwm_set_gpio_level(lcd_bl, value);
}

void ST7701::set_palette_colour(uint8_t entry, RGB888 colour) {
void ST7701::set_palette_colour(uint8_t entry, uint32_t colour) {
if (!palette) return;

// Note bit reversal is done by PIO.
Expand All @@ -750,19 +694,6 @@ void __not_in_flash_func(ST7701::start_frame_xfer)()
palette[entry] = encoded_colour;
}

void ST7701::set_palette_colour(uint8_t entry, const RGB& colour) {
if (!palette) return;

// Note bit reversal is done by PIO.
uint32_t encoded_colour =
((colour.r << 24) & 0xF8000000) | // R
((colour.g << 19) & 0x07E00000) | // G
((colour.b << 13) & 0x001F8000) | // B
((colour.r << 12) & 0x00004000); // Low bit of R

palette[entry] = encoded_colour;
}

void __no_inline_not_in_flash_func(ST7701::wait_for_vsync()) {
waiting_for_vsync = true;
while (waiting_for_vsync) __wfe();
Expand Down
27 changes: 18 additions & 9 deletions drivers/st7701/st7701.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@
#include "hardware/clocks.h"
#include "common/pimoroni_common.hpp"
#include "common/pimoroni_bus.hpp"
#include "libraries/pico_graphics/pico_graphics.hpp"

#include <cstdio>
#include <algorithm>
#include <cstring>

Expand All @@ -20,7 +20,13 @@ namespace pimoroni {
/// https://focuslcds.com/wp-content/uploads/Drivers/ST7701S.pdf
/// See ESPHome implementation:
/// https://github.com/esphome/esphome/blob/dev/esphome/components/st7701s
class ST7701 : public DisplayDriver {
class ST7701 {
public:
uint16_t width;
uint16_t height;
Rotation rotation;

private:
spi_inst_t *spi = PIMORONI_SPI_DEFAULT_INSTANCE;

//--------------------------------------------------
Expand Down Expand Up @@ -61,16 +67,19 @@ namespace pimoroni {
// Parallel init
ST7701(uint16_t width, uint16_t height, Rotation rotation, SPIPins control_pins, uint16_t* framebuffer, uint32_t* palette = nullptr,
uint d0=1, uint hsync=19, uint vsync=20, uint lcd_de = 21, uint lcd_dot_clk = 22);
virtual ~ST7701() {}
~ST7701() {}

void init();
void cleanup() override;
void update(PicoGraphics *graphics) override;
void partial_update(PicoGraphics *display, Rect region) override;
void set_backlight(uint8_t brightness) override;
void cleanup();

// Convert an RGBA8888 source into the RGB565 scanout buffer, staying behind
// the beam so a frame is never torn.
void update(const uint32_t *source);
void partial_update(const uint32_t *source, int x, int y, int w, int h);

void set_backlight(uint8_t brightness);

void set_palette_colour(uint8_t entry, RGB888 colour);
void set_palette_colour(uint8_t entry, const RGB& colour);
void set_palette_colour(uint8_t entry, uint32_t colour);

// The format is an 18-bit value: RGB566, followed by the final bit of red.
// It is MSB aligned, i.e. the top bit of red is in the MSB.
Expand Down
Loading
Loading