Make sure you have the latest CircuitPython firmware loaded for your board to use displayio. You can download it from the link below.

This example shows how to use a display on a breakout board using a SPI interface.

The Hard Way

Assuming you read through the datasheet(s) and somehow came up with the initialization sequence you needed for your display, you could do something like this.

This example was tested using a 2.4" TFT breakout wired to an Itsy Bitsy M4's hardware SPI pins. See here for display wiring information:

Download: file
import board
import displayio

# Release any previously configured displays
displayio.release_displays()

# Setup SPI bus
spi_bus = board.SPI()

# Digital pins to use
tft_cs = board.D10
tft_dc = board.D9

# Setup the display bus
display_bus = displayio.FourWire(spi_bus, command=tft_dc, chip_select=tft_cs)

# Setup the initialization sequence
# stolen from adafruit_ili9341.py
INIT_SEQUENCE = (
    b"\x01\x80\x80"            # Software reset then delay 0x80 (128ms)
    b"\xEF\x03\x03\x80\x02"
    b"\xCF\x03\x00\xC1\x30"
    b"\xED\x04\x64\x03\x12\x81"
    b"\xE8\x03\x85\x00\x78"
    b"\xCB\x05\x39\x2C\x00\x34\x02"
    b"\xF7\x01\x20"
    b"\xEA\x02\x00\x00"
    b"\xc0\x01\x23"            # Power control VRH[5:0]
    b"\xc1\x01\x10"            # Power control SAP[2:0];BT[3:0]
    b"\xc5\x02\x3e\x28"        # VCM control
    b"\xc7\x01\x86"            # VCM control2
    b"\x36\x01\x38"            # Memory Access Control
    b"\x37\x01\x00"            # Vertical scroll zero
    b"\x3a\x01\x55"            # COLMOD: Pixel Format Set
    b"\xb1\x02\x00\x18"        # Frame Rate Control (In Normal Mode/Full Colors)
    b"\xb6\x03\x08\x82\x27"    # Display Function Control
    b"\xF2\x01\x00"            # 3Gamma Function Disable
    b"\x26\x01\x01"            # Gamma curve selected
    b"\xe0\x0f\x0F\x31\x2B\x0C\x0E\x08\x4E\xF1\x37\x07\x10\x03\x0E\x09\x00" # Set Gamma
    b"\xe1\x0f\x00\x0E\x14\x03\x11\x07\x31\xC1\x48\x08\x0F\x0C\x31\x36\x0F" # Set Gamma
    b"\x11\x80\x78"            # Exit Sleep then delay 0x78 (120ms)
    b"\x29\x80\x78"            # Display on then delay 0x78 (120ms)
)

# Setup the Display
display = displayio.Display(display_bus, INIT_SEQUENCE, width=320, height=240)

#
# DONE - now you can use the display however you want
#

bitmap = displayio.Bitmap(320, 240, 2)

palette = displayio.Palette(2)
palette[0] = 0
palette[1] = 0xFFFFFF

for x in range(10, 20):
    for y in range(10, 20):
      bitmap[x, y] = 1

tile_grid = displayio.TileGrid(bitmap, pixel_shader=palette)

group = displayio.Group()
group.append(tile_grid)
display.show(group)
display.refresh_soon()

The Easy Way

Use a driver instead. This will take care of the initialization sequence for you. Here we use the ILI9341 driver.

Download: file
    import board
import displayio
import adafruit_ili9341

# Release any previously configured displays
displayio.release_displays()

# Setup SPI bus
spi_bus = board.SPI()

# Digital pins to use
tft_cs = board.D10
tft_dc = board.D9

# Setup the display bus
display_bus = displayio.FourWire(spi_bus, command=tft_dc, chip_select=tft_cs)

# Setup the Display
display = adafruit_ili9341.ILI9341(display_bus, width=320, height=240)

#
# DONE - now you can use the display however you want
#

bitmap = displayio.Bitmap(320, 240, 2)

palette = displayio.Palette(2)
palette[0] = 0
palette[1] = 0xFFFFFF

for x in range(10, 20):
    for y in range(10, 20):
      bitmap[x, y] = 1

tile_grid = displayio.TileGrid(bitmap, pixel_shader=palette)

group = displayio.Group()
group.append(tile_grid)
display.show(group)
display.refresh_soon()
  

For displays in the Adafruit shop, there should be (or will be) a CircuitPython driver for each one. One driver might handle more than one product. They are designated by chipset number, like ILI9341. Look at the Adafruit product page to see which chipset the Adafruit product uses and then look for the corresponding Adafruit CircuitPython driver. Then you do not have to deal with the low level initialization.

If you have a non-Adafruit display, you might be able to use an existing CircuitPython driver if it uses the same chipset as one of the available drivers. This isn't guaranteed though as a manufacturer might have made changes to a board not supplied by Adafruit. So you might need to experiment more to see if the code may work. This isn't to get you to buy Adafruit's displays, more like a friendly note that more tinkering may be needed as things may not be tested out like Adafruit displays.

The CircuitPython team encourages contributors to add drivers for displays not currently handled in the CircuitPython library bundle. If you write a driver, it can be shared with others with the same display, contributing back to the community. Isn't Open Source helpful? We think so.

This guide was first published on Apr 30, 2019. It was last updated on Apr 30, 2019.
This page (External Display) was last updated on Oct 24, 2020.