Picante: a Pi Pico-based handheld project
In The Beginning...
I'd been experimenting with SPI displays on the Micro:bit before now and was lamenting how little RAM they have.
So when I read about the Pi Pico's whopping great 264KB of RAM I immediately wanted to make a DOS-a-like handheld game platform. It easily has enough RAM for a full 320x240x8bpp frame buffer and at 133MHz it's about the same speed as an original-series Pentium. Plus, it's dual core!
All those figures made me think it would be an absolute doddle to make a DOS-a-like.
...but then I started to think about how it would actually be used. Sure, I could make it so that C programmers could make games for it, but that might just end up with curiosity value.
But what if I could make it so that the students I teach could program games in Python? That way it would have loads of inspiration power whilst also having high educational value!
With the magic mix of both idea and plausible use case, I set about making a prototype.
The Hardware
Some component-orders and shoddy soldering later, I had a the beginnings of a v0.1 prototype:
The screen is a 2.8" 320x240 Adafruit display from Pimoroni*. It has a micro-SD slot and resistive touchscreen that I'll probably play around with later. The 90-degree headers, microswitches and prototype board are cheapies off eBay.
I also bought an I2S audio module, but I have yet to try that out.
* I don't get any money or perks from Pimoroni or Adafruit - I just like their stuff.
Getting it running
Flashing the micropython uf2 firmware was an absolute doddle (see here). Installing thonny was a little bit annoying because the Ubuntu version is so out of date that I couldn't follow the instructions I found. However, it was pretty easy to install the latest version via their website.
I used rdagger's ILI9341 micropython library and after some initial problems (I had wired up MISO/MOSI the wrong way round, forgotten to short-out IM1, IM2 and IM3 and one of my cheap-ass wires was b0rked) I got the demo_shapes.py code running.
Thonny's REPL window was really useful for figuring out a way through those initial problems. A lot of complications could've been avoided if I had actually read the display instructions properly the first time, but y'know - where's the fun in that?
Micropython problems
So I had micropython code driving the display, but it was sloooooow. I had seen much faster framerates online, and it turned out I needed a full frame buffer in RAM. The display works at 16bpp, so a full framebuffer needs 320x240x2 = 153600 bytes. No problem, right? I have 256KB to play with!
Wrong.
As impressive as micropython is (and it really is!), it sits on enough RAM to make a full framebuffer impossible. But I really wanted people to be able to make little games in Python, so I had to figure another way round it.
Towards a Framebuffer Solution
So I figured I would have an 8bpp framebuffer (76800 bytes) and a 16bpp stripe that I could expand the 8bpp into for transfer (repeating once for each stripe on the screen). I got it working in Python, but it was prohibitively slow. I needed to get the 8bpp to 16bpp expansion running in native code.
Framebuffer handling in native code
A really impressive thing about micropython is its ability to put native code in a .mpy file that is imported just like a Python module. Unfortunately I needed to re-compile the firmware with some tweaks for the RP2040, but I did get it working.
The render buffer is in a strange format: RGBA:2321 (alpha not implemented yet), and later I intend to do colour-palettes because who doesn't like palette cycling animations?
Adding Sprite 'Blitting'
So, the latest thing I've added is C code to clear the 8bpp render buffer and 'blit' a 32x32 sprite to it. A full-buffer clear and blitting a single sprite (on a single core) runs at just under 19fps!
This thing might just work after all!
Comments
Post a Comment