My children are adults, long departed the nest. That doesn’t seem to stop them from bringing me everyday problems as they come up. Last week my son Felix hit a snag using an OLED display. It worked fine for a while, then simply stopped. Here it is in the happy time when it was not an inert black rectangle.
Felix had it hooked up to a Pico, as well as to a SPIDriver for bringup. Both worked fine, then both eventually stopped working at all. The display is an SSD1306, which uses the pretty-common “SPI plus a couple of control lines” hookup. Lots of panels use the same kind of scheme.
When we started working the problem, the suspicion was that something might have gone wrong with the software. After all, it’s very easy to make an innocuous-looking change that stops the hardware from working. I do it about once a day, so it seemed not completely impossible that my offspring had done the same. The apple never falls far from the tree, as they say.
What we really wanted was to run some known-good driver with the SPIDriver and the SSD1306. Enter Adafruit, and their impressive commitment to Open Source. They provide drivers and examples for a huge range of hardware. Including this ready-to-run Python module for the SSD1306.
In the past, I’ve taken the Adafruit Python drivers and ported them to SPIDriver’s API. The hardware SPI interface is a little different, but after a bit of rearranging, it generally works. But then what? I can’t port every driver — there are hundreds, and the maintenance would be continuous.
How much better it would be if the all the wonderful Adafruit libraries just worked with SPIDriver? Then we could run the sample code unchanged, and really confirm if the problem was software or hardware. And even better, anyone else who wanted to use an Adafruit driver could run with their SPIDriver.
The way Adafruit solved the portability problem between CircuitPython (on microcontrollers) and CPython (on PCs and SBCs) is using a library called Blinka. Blinka runs on the PC, and reproduces all the hardware interfaces (like SPI, I2C and GPIOs). Blinka supports all kinds of hardware, including many USB adapters. Adding SPIDriver and I2CDriver support to the list should be fairly straightforward.
And it was. After a bit of hacking, I had SPIDriver working under Blinka.
Then I took Adafruit’s example code for the SSD1306, changing the pin assignments for SPIDriver:
import board
import fourwire
import adafruit_displayio_ssd1306
import displayio
import busio
displayio.release_displays()
# This pinout is for the SPIDriver
spi = busio.SPI(board.SCK, board.MOSI)
tft_cs = board.CS
tft_dc = board.A
tft_reset = board.B
display_bus = fourwire.FourWire(spi, command=tft_dc, chip_select=tft_cs, reset=tft_reset, baudrate=1000000)
display = adafruit_displayio_ssd1306.SSD1306(display_bus, width=128, height=64)
and it came up immediately
then I added some code to draw a bitmap:
splash = displayio.Group()
bitmap = displayio.Bitmap(128, 64, 1) # 1 color slot
palette = displayio.Palette(2)
palette[0] = 0x000000
palette[1] = 0xFFFFFF
from PIL import Image
pixels = Image.open(”logo128.png”).load()
for y in range(64):
for x in range(128):
bitmap[x, y] = pixels[x, y]
background = displayio.TileGrid(bitmap, pixel_shader=palette)
splash.append(background)
display.root_group = splash
and here’s the proof. It all just works.
Next step is adding I²CDriver support to Blinka as well, then I’ll submit a PR to the maintainers of Adafruit_Blinka.
I can possibly help create a bridge to the MicroPython `machine` APIs too, if that's useful. Unfortunately, CircuitPython uses a similar-but-different API to MicroPython for SPI/I2C.
Nice!