:information_source: Latest test builds - https://github.com/pimoroni/pimoroni-pico/actions/runs/2514970828 (now with MicroPython v1.19, PIO accelerated ST7789 parallel output, performance tweaks, Tufty2040 build, updated 17 Jun, 11:00 GMT)
A 340x240 16-bit framebuffer uses a whopping 150k of RAM. This is relatively okay in C++ but causes issues in MicroPython where the gc_heap is only 192k.
This set of changes allow you to use a native 16-bit, smaller 8-bit, or tiny 4-bit framebuffer for most of our SPI displays. This lets you balance memory usage with available colours and display performance.
This change renames the st7789
module to picographics
in MicroPython.
Bringup
Here's an example bringup for the 160x80 ST7735 LCD in true-colour RGB565 mode:
from picographics import PicoGraphics, DISPLAY_LCD_160X80, PEN_RGB565
display = PicoGraphics(display=DISPLAY_LCD_160X80, pen_type=PEN_RGB565)
The above uses 25K of RAM and supports 65K colours.
from picographics import PicoGraphics, DISPLAY_LCD_160X80, PEN_P4
display = PicoGraphics(display=DISPLAY_LCD_160X80, pen_type=PEN_P4)
The above uses 6.25K of RAM and supports 8 colours!
Custom Pins
You can use your own pins for supported SPI or Parallel displays.
You must construct an SPIBus or ParallelBus object using the pimoroni_bus
module:
from pimoroni_bus import SPIBus
from picographics import PicoGraphics, DISPLAY_PICO_EXPLORER, PEN_RGB332
spibus = SPIBus(cs=17, dc=16, sck=18, mosi=19)
display = PicoGraphics(display=DISPLAY_PICO_EXPLORER, bus=spibus, pen_type=PEN_RGB332)
Saving More RAM / Doing Weird Things
If you need to temporarily claim back RAM from PicoDisplay, or use multiple framebuffers, you can use set_framebuffer
to set/clear the region of memory it uses internally.
:warning: DO NOT attempt to draw when no framebuffer is set. You'll have a bad time!
from picographics import PicoGraphics, get_buffer_size, DISPLAY_LCD_160X80, PEN_P4
# Big enough for 160 * 80 at 4-bits per pixel
buf = bytearray(get_buffer_size(DISPLAY_LCD_160X80, PEN_P4))
display = PicoGraphics(display=DISPLAY_LCD_160X80, pen_type=PEN_P4, buffer=buf)
# Detach the framebuffer from PicoGraphics
display.set_framebuffer(None)
# Set a different framebuffer
buf2 = bytearray(int(160 * 80 / 2))
display.set_framebuffer(buf2)
Displaying JPEG files
Thanks to JPEGDEC - https://github.com/bitbank2/JPEGDEC - you can load and display JPEG files via PicoGraphics, like so:
import picographics
import jpegdec
lcd = picographics.PicoGraphics(display=picographics.DISPLAY_PICO_EXPLORER, rotate=0, pen_type=picographics.PEN_RGB565)
# Create a new JPEG decoder for our PicoGraphics
j = jpegdec.JPEG(lcd)
# Open the JPEG file
j.open_file("240.jpeg")
# Decode the JPEG
j.decode(0, 0, 0)
# Display the result
lcd.update()
The arguments to "decode" are x-offset, y-offset and scale. Value scale values are:
JPEG_SCALE_HALF = 2
JPEG_SCALE_QUARTER = 4
JPEG_SCALE_EIGHTH = 8
When you call decode
the jpeg decoder makes a best effort to draw into your PicoGraphics surface in a sensible pixel format.
Sprites
Pico Graphics has very basic support for 128x128 spritesheets in PEN_RGB332 mode:
from picographics import PicoGraphics, DISPLAY_TUFTY_2040, PEN_RGB332
import time
lcd = PicoGraphics(DISPLAY_TUFTY_2040, pen_type=PEN_RGB332)
# Reserve a large enough buffer
sprites = bytearray(128 * 128)
# Load a spritesheet into the buffer
open("s4m_ur4i-dingbads.rgb332", "rb").readinto(sprites)
#open("s4m_ur4i-pirate-characters.rgb332", "rb").readinto(sprites)
# Tell PicoGraphics to use this new spritesheet
lcd.set_spritesheet(sprites)
# Draw a sprite
lcd.sprite(0, 0, 10, 10, 255)
The function sprite
takes:
- the X/Y index of a sprite in the spritesheet. These should be between 0-15.
- the X/Y coordinates to draw the sprite on screen.
- the colour to treat as transparent
Supported Displays
The current list of supported displays is:
DISPLAY_ENVIRO_PLUS
DISPLAY_LCD_160X80
DISPLAY_LCD_240X240
DISPLAY_PICO_DISPLAY
DISPLAY_PICO_DISPLAY_2
DISPLAY_PICO_EXPLORER
DISPLAY_ROUND_LCD_240X240
DISPLAY_TUFTY_2040
Supported Pen Types
PEN_P4
- 4-bit packed, with an 8 colour palette. This is commonly used for 7/8-colour e-ink displays or driving large displays with few colours.
PEN_P8
- 8-bit, with a 256 colour palette. Great balance of memory usage versus available colours. You can replace palette entries on the fly.
PEN_RGB332
- 8-bit, with a fixed 256 colour RGB332 palette. Great for quickly porting an RGB565 app to use less RAM. Limits your colour choices, but is easier to grok.
PEN_RGB565
- 16-bit, 65K "True Colour." Great for rainbows, gradients and images but comes at the cost of RAM!
TODO
- [ ] Add some new documentation here!
- [x] Handle a failure to allocate a palette colour (return -1 if we've run out of palette space?)
- [x] Provide MicroPython bindings for palette put/get methods
- [x] Provide a way to initialize a specific display type's args automatically, eg:
ST7789(display=SPI_LCD_240X240)
- [x] Update all examples to remove
set_pen(r, g, b)
since this is meaningless and deprecated
- [ ] Write a migration guide
- [ ] Update documentation
- [x] Remove obsolete functions from Pico Display & Pico Explorer classes- should be header-only defines of pins
- [ ] Create a C++ "Buzzer" driver to mirror the MicroPython one and replace "pico_explorer.set_tone()"
TO TEST
- [x] Pico Display 2.0 - All rotations
- [x] Pico Display - All rotations
- [x] SPI LCD 240 x 240 - All rotations
- [x] Pico Explorer - All rotations