New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
ST7789 / PicoGraphics rewrite - Support for 4-bit, 8-bit and 16-bit framebuffers and more. #373
Conversation
@alphanumeric007 this may be relevant to your interests! |
Yes, that may be a big advantage for me. I pretty well stick to the basic colors. Red, Yellow, Green, and Blue. And on occasion Orange and Violet. I have a PICO Breakout Garden Base, and a couple of the 1.5" st7789 LCD Breakouts I can use for testing if that helps? |
No great need to test in depth, but it should get you back about 75K of RAM in your dual display setup. Or let you have separate framebuffers if you really want! Your limited use of colour should make it pretty much a drop-in replacement and you wont notice much. To use your specified colours you can trash the default palette:
And specify not to truncate new colours to RGB332 on creation:
Or create all your colours up-front, which returns an 0-255 palette index:
I might actually force this behavior at the expense of upsetting a few people who've used I hate breaking compatibility with user code, but we've got some bad design choices to fix! |
f9c53e6
to
b1d99af
Compare
Nice idea! It makes the above board far more useful in MicroPython. |
You can also add a way to detach and free the FB temporarily. Since the display contains its own RAM, if we TEMPORARILY need lots of RAM (eg. to load an image to prepare a printout to a thermal printer, calculate a QR code) it would be of great help to do the following: |
Right now you can pass a buffer into the library which it'll hold a reference to. It should be possible to provide methods to force that to a nullptr so you can GC the buffer. Maybe something like: st7789.set_buffer(None) vs st7789.set_buffer(some_byte_array) I'm currently rewriting swathes of this code to make the duality of RGB332 default palettes and RGB565 user palettes actually workable so I'll see if I can squeeze this in. |
Thanks a lot! |
Done as described above. Don't uh... try to draw while the framebuffer is unset 😆 the library will not check. |
bf1c68d
to
07a96b7
Compare
Thanks Phil =), very much appreciated. I will tinker with this over the weekend. Just poured myself a grog of Captain Morgan's Dark Rum. It's the Pirate thing to do on a Friday evening. ;) |
I've updated the Pico Display Python examples to demonstrate some of the changes. To use custom colours: display.set_palette_mode(st7789.PALETTE_USER)
BLACK = display.create_pen(0, 0, 0)
CUSTOM = BLACK + 1
display.set_palette(CUSTOM, st7789.RGB565(255, 0, 255)) # Set the CUSTOM entry to RED
display.set_pen(CUSTOM) To easy init a display: display = st7789.ST7789(st7789.DISPLAY_PICO_DISPLAY, rotate=0) I am trying to support all rotations, but getting the settings right is tricky 😆 much testomg and tweaking to do. |
One wrinkle for me is I have display1 and display2. So I'll have to do something like the following. BLACK = display.create_pen(0, 0, 0) I'm thinking I may have to go with two 8 bit display buffers? This isn't a complaint, just a FYI from the guy that doesn't do things the normal way. ;) |
9c9127c
to
b61f7b5
Compare
@alphanumeric007 in the default RGB332 any colour you create on one display will be the same on the other, so you can create them once and use them on both. Internally it just crushes that colour to its RGB332 representation, then looks up and returns its index into the palette. |
Latest build, slot param - and most others - have gone. All SPI or Parallel pins must be supplied as a "pimoroni_bus". This has a helper factory to create pins for slots, see below: import st7789
import pimoroni_bus
BUF = bytearray(320 * 240)
BUS_BG_FRONT = pimoroni_bus.SPISlot(0)
BUS_BG_BACK = pimoroni_bus.SPISlot(1)
display = st7789.ST7789(st7789.DISPLAY_PICO_DISPLAY_2, rotate=90, buf=BUF, bus=BUS_BG_FRONT)
display = st7789.ST7789(st7789.DISPLAY_PICO_DISPLAY_2, rotate=90, buf=BUF, bus=BUS_BG_BACK)
To get the WIDTH/HEIGHT of a display - for portable code - use: WIDTH, HEIGHT = display.get_bounds() I've finally arrived at something I'm more or less happy with for the Python API. Hopefully it wont change much from here. Albeit there are certainly some considerable DMA performance gains to be had for the palette-based display refresh. My sinuses are screaming at me to stick a steam train up each nostril. Will clean this up for release this coming week. |
Ok, so how do I create the color once, and use it on both displays, code wise? Looks up at very dark light bulb hovering over head. |
@alphanumeric007 for def create_pen(r, g, b):
display1.create_pen(r, g, b)
return display2.create_pen(r, g, b) There's no reason why the palettes shouldn't stay completely in sync. For It stuck me last night that you can just draw into one display since they share the same back buffer, but calling |
dd3ac0e
to
f1b3f43
Compare
I just now figured it out. Well one way to do it anyway. This is with the stock framebuffer, not the new 8 bit one. |
Ah with the stock framebuffer Internally it just does: uint16_t p = ((r & 0b11111000) << 8) |
((g & 0b11111100) << 3) |
((b & 0b11111000) >> 3);
return __builtin_bswap16(p); Whereas the new 8-bit stuff - by default (in RGB332 mode) - does: return (r & 0b11100000) | ((g & 0b11100000) >> 3) | ((b & 0b11000000) >> 6) Either way a created pen is transferable, the only differences is how many bits of colour information are thrown away. |
I wasn't using create_pen at all in my current code. I was doing the (255, 255, 255) thing. |
Just flashed the new uf2 to that Pico. I added / edited to this BUF = bytearray(320 * 240) BUS_BG_FRONT = pimoroni_bus.SPISlot(0) display = st7789.ST7789(st7789.DISPLAY_PICO_DISPLAY_2, rotate=90, buf=BUF, bus=BUS_BG_FRONT) |
Sorry the argument |
Ah, OK, that fixed it. Working now with that latest 8bit test uf2 file. Next step is to do the 8-bit edits etc. BUS_BG_FRONT = pimoroni_bus.SPISlot(0) display = st7789.ST7789(st7789.DISPLAY_PICO_DISPLAY_2, rotate=270, buffer=BUF, bus=BUS_BG_FRONT) |
I hate to be a pest, but I'm a bit lost with all the different bits of code posted above? |
A first port of cal would be to replace any instance of: display.set_pen(r, g, b) With a: COLOR = display.create_pen(r, g, b)
display.set_pen(COLOR) Then give all your colours sensible names and move their creation up to the top of the file after display init. You can also just opt for the easy way and do: display.set_pen(display.create_pen(r, g, b)) |
I did flash_nuke it with the Pi Foundation uf2 file, right after posting the above. I can unplug, plug back in, turn off and back on, and no error message or any issues in Thonny. Same cables as before and same USB ports used on the PC. I'm just about to drop the latest test uf2 onto it and put my latest file back on it. I'll post back how that goes. |
Dropped "pimoroni-picolipo_16mb-4f4b88501a92daf32b64808fa0d4055c301cfe41-micropython.uf2" onto it and so far so good. Started and stopped it a few times with no issues. Unplugged it and plugged it back and no error message. My code is running fine with PEN_RGB332 and the buffer code added back in. P8 and P4 won't work with this uf2 though? |
Where does one find a 16mb flash nuke file? And does it actually need to be used? |
I might need to avail myself of a 16MB Pico LiPo to see if I can replicate. Latest builds are: https://github.com/pimoroni/pimoroni-pico/actions/runs/2503525236 Which may or may not have working SH1107 128x128 support. |
I'm going to try and pick up a new USB C cable somewhere today. The weak link may be the Micro USB to USB C adapter I'm using. It's the one you guys sell. I bought it so I could power a Pi 4 with a Pi 3 power supply. Then repurposed it. You wouldn't think the version of uf2 would matter though if it was faulty? Stranger things have happened though so I figure I better get something better suited for purpose. I went Pico Lipo because i wanted the built in battery charger, run on battery option. ;) |
Just added I2C OLED support into MicroPython. Needs some fleshing out, but it works with our 128x128 OLED: from picographics import PicoGraphics, DISPLAY_I2C_OLED_128X128, PEN_1BIT
from pimoroni_i2c import PimoroniI2C
bus = PimoroniI2C(4, 5)
display = PicoGraphics(DISPLAY_I2C_OLED_128X128, pen_type=PEN_1BIT, bus=bus)
display.set_pen(1)
display.clear()
display.set_font("gothic")
display.set_pen(0)
display.text("Hello World", 0, 10, 0, 0.5)
display.update() This means you can use this little display with all of our boards sporting a Stemma/Qwiic connector. |
It looks like what ever Device Descriptor issue I'm having is at my end. |
Make display updates around 4x faster. Requires a PIO + SM to run Tufty 2040.
bfad1b4
to
f1f881a
Compare
Add board fixups including Pico SDK board .h and MicroPython board dir since these are not yet upstream.
f1f881a
to
122e57a
Compare
384e56b
to
0258247
Compare
Hey Phil, I have a tufty ;) question for you, but have lost that e-mail address you gave me. |
I have a.... tufty answer for you... 👀 |
dca1a02
to
64c3387
Compare
Co-authored-by: Phil Howard <phil@pimoroni.com>
Co-authored-by: ZodiusInfuser <christopher.parrott2@gmail.com>
84ae291
to
f20049a
Compare
Co-authored-by: Phil Howard <phil@pimoroni.com>
9bc9b85
to
27f1ecb
Compare
Merged and released as v1.19.0 - https://github.com/pimoroni/pimoroni-pico/releases/tag/v1.19.0 Open a new issue if you find any other bugs/have any other questions. Thaannkk yooou to all involved! |
ℹ️ 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 topicographics
in MicroPython.Bringup
Here's an example bringup for the 160x80 ST7735 LCD in true-colour RGB565 mode:
The above uses 25K of RAM and supports 65K colours.
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: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.Displaying JPEG files
Thanks to JPEGDEC - https://github.com/bitbank2/JPEGDEC - you can load and display JPEG files via PicoGraphics, like so:
The arguments to "decode" are x-offset, y-offset and scale. Value scale values are:
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:
The function
sprite
takes: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
ST7789(display=SPI_LCD_240X240)
set_pen(r, g, b)
since this is meaningless and deprecatedTO TEST