CircuitPython Code

It is very easy to use the RA8875 with CircuitPython. We recommend using an M4 based board due to the size of library.

CircuitPython Microcontroller Wiring

Start by connecting the power pins

  • 3-5V Vin connects to the Feather's USB pin
  • GND connects to the Feather's ground
  • SCK connects to SPI clock. On the Feather M4 Express, thats also SCK
  • MISO connects to SPI MISO. On the Feather M4 Express, thats MI
  • MOSI connects to SPI MOSI. On the Feather M4 Express, thats MO
  • CS connects to our SPI Chip Select pin. We'll be using Digital 9 but you can later change this to any pin
  • RST connects to our Reset pin. We'll be using Digital 10 but you can later change this pin as well.
  • INT connects to our Interrupt pin. This pin is actually optional, but improves the accuracy. We'll be using Digital 11 but you can later change this pin too.

That's it! You don't need to connect any of the other pins.

Library Installation

You'll need to install the Adafruit CircuitPython RA8875 library on your CircuitPython board. 

You will also need to install the Adafruit CircuitPython BusDevice library which is the only dependency.

First, make sure you are running the latest version of Adafruit CircuitPython for your board.

Next, you'll need to install the necessary libraries to use the hardware--carefully follow the steps to find and install these libraries from Adafruit's CircuitPython library bundle.  Our introduction guide has a great page on how to install the library bundle for both express and non-express boards.

Remember for non-express boards, you'll need to manually install the necessary libraries from the bundle:

  • adafruit_ra8875
  • adafruit_bus_device

Before continuing make sure your board's lib folder or root filesystem has the adafruit_ra8875 and adafruit_bus_device folders copied over.

Usage

To demonstrate the usage of the display, we'll use the example python script included with the library. 

# Quick test of RA8875 with Feather M4
import time
import busio
import digitalio
import board

import adafruit_ra8875.ra8875 as ra8875
from adafruit_ra8875.ra8875 import color565

BLACK = color565(0, 0, 0)
RED = color565(255, 0, 0)
BLUE = color565(0, 255, 0)
GREEN = color565(0, 0, 255)
YELLOW = color565(255, 255, 0)
CYAN = color565(0, 255, 255)
MAGENTA = color565(255, 0, 255)
WHITE = color565(255, 255, 255)

# Configuration for CS and RST pins:
cs_pin = digitalio.DigitalInOut(board.D9)
rst_pin = digitalio.DigitalInOut(board.D10)
int_pin = digitalio.DigitalInOut(board.D11)

# Config for display baudrate (default max is 6mhz):
BAUDRATE = 6000000

# Setup SPI bus using hardware SPI:
spi = busio.SPI(clock=board.SCK, MOSI=board.MOSI, MISO=board.MISO)

# Create and setup the RA8875 display:
display = ra8875.RA8875(spi, cs=cs_pin, rst=rst_pin, baudrate=BAUDRATE)
display.init()

display.fill(RED)
time.sleep(0.500)
display.fill(YELLOW)
time.sleep(0.500)
display.fill(BLUE)
time.sleep(0.500)
display.fill(CYAN)
time.sleep(0.500)
display.fill(MAGENTA)
time.sleep(0.500)
display.fill(BLACK)
display.circle(100, 100, 50, BLACK)
display.fill_circle(100, 100, 49, BLUE)

display.fill_rect(10, 10, 400, 200, GREEN)
display.rect(10, 10, 400, 200, BLUE)
display.fill_round_rect(200, 10, 200, 100, 10, RED)
display.round_rect(200, 10, 200, 100, 10, BLUE)
display.pixel(10, 10, BLACK)
display.pixel(11, 11, BLACK)
display.line(10, 10, 200, 100, RED)
display.fill_triangle(200, 15, 250, 100, 150, 125, YELLOW)
display.triangle(200, 15, 250, 100, 150, 125, BLACK)
display.fill_ellipse(300, 100, 100, 40, BLUE)
display.ellipse(300, 100, 100, 40, RED)
display.curve(50, 100, 80, 40, 2, BLACK)
display.fill_curve(50, 100, 78, 38, 2, WHITE)

display.txt_set_cursor(display.width // 2 - 200, display.height // 2 - 20)
display.txt_trans(WHITE)
display.txt_size(2)
testvar = 99
display.txt_write("Player Score: " + str(testvar))

display.touch_init(int_pin)
display.touch_enable(True)

x_scale = 1024 / display.width
y_scale = 1024 / display.height

# Main loop:
while True:
    if display.touched():
        coords = display.touch_read()
        display.fill_circle(
            int(coords[0] / x_scale), int(coords[1] / y_scale), 4, MAGENTA
        )
        display.txt_color(WHITE, BLACK)
        display.txt_set_cursor(display.width // 2 - 220, display.height // 2 - 20)
        display.txt_size(2)
        display.txt_write(
            "Position ("
            + str(int(coords[0] / x_scale))
            + ", "
            + str(int(coords[1] / y_scale))
            + ")"
        )

We start out by importing any libraries we want to use in our code. This includes a special function that converts 24-bit color into 16-bit color which is what this display uses.

Download: file
import time
import busio
import digitalio
import board

import adafruit_ra8875.ra8875 as ra8875
from adafruit_ra8875.ra8875 import color565

Next, we define a bunch of colors for ease of reuse.

Download: file
BLACK = color565(0, 0, 0)
RED = color565(255, 0, 0)
BLUE = color565(0, 255, 0)
GREEN = color565(0, 0, 255)
YELLOW = color565(255, 255, 0)
CYAN = color565(0, 255, 255)
MAGENTA = color565(255, 0, 255)
WHITE = color565(255, 255, 255)

Next, we initialize SPI and the display. 6 MHz was about the fastest that it would stably run in CircuitPython. Initialization is much easier in CircuitPython. By default this runs for a display of 800x480 pixels in size. If you have a display of a different size such as the 480x272, you could pass the width and height parameters into the constructor.

Download: file
# Configuration for CS and RST pins:
cs_pin = digitalio.DigitalInOut(board.D9)
rst_pin = digitalio.DigitalInOut(board.D10)
int_pin = digitalio.DigitalInOut(board.D11)

# Config for display baudrate (default max is 6mhz):
BAUDRATE = 6000000

# Setup SPI bus using hardware SPI:
spi = busio.SPI(clock=board.SCK, MOSI=board.MOSI, MISO=board.MISO)

# Create and setup the RA8875 display:
display = ra8875.RA8875(spi, cs=cs_pin, rst=rst_pin, baudrate=BAUDRATE)
display.init()

Next, we fill the screen with a few different colors.

Download: file
display.fill(RED)
time.sleep(0.500)
display.fill(YELLOW)
time.sleep(0.500)
display.fill(BLUE)
time.sleep(0.500)
display.fill(CYAN)
time.sleep(0.500)
display.fill(MAGENTA)
time.sleep(0.500)

After that, we clear the screen and draw a bunch of different shapes to demonstrate the hardware accelerated shape drawing functions.

Download: file
display.fill(BLACK)
display.circle(100, 100, 50, BLACK)
display.fill_circle(100, 100, 49, BLUE)

display.fill_rect(11, 11, 398, 198, GREEN)
display.rect(10, 10, 400, 200, BLUE)
display.fill_round_rect(200, 10, 200, 100, 10, RED)
display.round_rect(199, 9, 202, 102, 12, BLUE)
display.pixel(10, 10, BLACK)
display.pixel(11, 11, BLACK)
display.line(10, 10, 200, 100, RED)
display.triangle(200, 15, 250, 100, 150, 125, BLACK)
display.fill_triangle(200, 16, 249, 99, 151, 124, YELLOW)
display.ellipse(300, 100, 100, 40, RED)
display.fill_ellipse(300, 100, 98, 38, BLUE)
display.curve(50, 100, 80, 40, 2, BLACK)
display.fill_curve(50, 100, 78, 38, 2, WHITE)

Next, we draw some text on the screen. Please note that unlike the Arduino library, mode changing is automatically handled in the library.

Download: file
display.txt_set_cursor(display.width // 2 - 200, display.height // 2 - 20)
display.txt_trans(WHITE)
display.txt_size(2)
testvar = 99
display.txt_write("Player Score: " + str(testvar))

Finally, touch is enabled and in the main loop we read the coordinates of the text, display where the touch occurred on the screen and draw circles in that location.

Download: file
display.touch_init(int_pin)
display.touch_enable(True)

x_scale = 1024 / display.width
y_scale = 1024 / display.height

# Main loop:
while True:
    if display.touched():
        coords = display.touch_read()
        display.fill_circle(int(coords[0]/x_scale), int(coords[1]/y_scale), 4, MAGENTA)
        display.txt_color(WHITE, BLACK)
        display.txt_set_cursor(display.width // 2 - 220, display.height // 2 - 20)
        display.txt_size(2)
        display.txt_write("Position (" + str(int(coords[0]/x_scale)) + ", " +
                          str(int(coords[1]/y_scale)) + ")")

Loading a Bitmap

In this next example, we'll decode and load a bitmap that is stored as a file on the MicroController. Although the image is included in the examples folder of the library, we have provided a link to it for your convenience.

# Quick bitmap test of RA8875 with Feather M4
import busio
import digitalio
import board

import adafruit_ra8875.ra8875 as ra8875
from adafruit_ra8875.ra8875 import color565

try:
    import struct
except ImportError:
    import ustruct as struct

WHITE = color565(255, 255, 255)

# Configuration for CS and RST pins:
cs_pin = digitalio.DigitalInOut(board.D9)
rst_pin = digitalio.DigitalInOut(board.D10)

# Config for display baudrate (default max is 6mhz):
BAUDRATE = 8000000

# Setup SPI bus using hardware SPI:
spi = busio.SPI(clock=board.SCK, MOSI=board.MOSI, MISO=board.MISO)

# Create and setup the RA8875 display:
display = ra8875.RA8875(spi, cs=cs_pin, rst=rst_pin, baudrate=BAUDRATE)
display.init()
display.fill(WHITE)


def convert_555_to_565(rgb):
    return (rgb & 0x7FE0) << 1 | 0x20 | rgb & 0x001F


class BMP:
    def __init__(self, filename):
        self.filename = filename
        self.colors = None
        self.data = 0
        self.data_size = 0
        self.bpp = 0
        self.width = 0
        self.height = 0
        self.read_header()

    def read_header(self):
        if self.colors:
            return
        with open(self.filename, "rb") as f:
            f.seek(10)
            self.data = int.from_bytes(f.read(4), "little")
            f.seek(18)
            self.width = int.from_bytes(f.read(4), "little")
            self.height = int.from_bytes(f.read(4), "little")
            f.seek(28)
            self.bpp = int.from_bytes(f.read(2), "little")
            f.seek(34)
            self.data_size = int.from_bytes(f.read(4), "little")
            f.seek(46)
            self.colors = int.from_bytes(f.read(4), "little")

    def draw(self, disp, x=0, y=0):
        print("{:d}x{:d} image".format(self.width, self.height))
        print("{:d}-bit encoding detected".format(self.bpp))
        line = 0
        line_size = self.width * (self.bpp // 8)
        if line_size % 4 != 0:
            line_size += 4 - line_size % 4
        current_line_data = b""
        with open(self.filename, "rb") as f:
            f.seek(self.data)
            disp.set_window(x, y, self.width, self.height)
            for line in range(self.height):
                current_line_data = b""
                line_data = f.read(line_size)
                for i in range(0, line_size, self.bpp // 8):
                    if (line_size - i) < self.bpp // 8:
                        break
                    if self.bpp == 16:
                        color = convert_555_to_565(line_data[i] | line_data[i + 1] << 8)
                    if self.bpp == 24 or self.bpp == 32:
                        color = color565(
                            line_data[i + 2], line_data[i + 1], line_data[i]
                        )
                    current_line_data = current_line_data + struct.pack(">H", color)
                disp.setxy(x, self.height - line + y)
                disp.push_pixels(current_line_data)
            disp.set_window(0, 0, disp.width, disp.height)


bitmap = BMP("/ra8875_blinka.bmp")
x_position = (display.width // 2) - (bitmap.width // 2)
y_position = (display.height // 2) - (bitmap.height // 2)
bitmap.draw(display, x_position, y_position)

Let's dive in and take a look at how the code works. Just like the previous example, we start out by importing the libraries we need. In this case, we also need struct to help with color encoding.

Download: file
import busio
import digitalio
import board

import adafruit_ra8875.ra8875 as ra8875
from adafruit_ra8875.ra8875 import color565
try:
    import struct
except ImportError:
    import ustruct as struct

White is the only color we will use, so we define that here.

Download: file
WHITE = color565(255, 255, 255)

Next, we initialize the screen and set the background to white.

Download: file
# Configuration for CS and RST pins:
cs_pin = digitalio.DigitalInOut(board.D9)
rst_pin = digitalio.DigitalInOut(board.D10)

# Config for display baudrate (default max is 6mhz):
BAUDRATE = 6000000

# Setup SPI bus using hardware SPI:
spi = busio.SPI(clock=board.SCK, MOSI=board.MOSI, MISO=board.MISO)

# Create and setup the RA8875 display:
display = ra8875.RA8875(spi, cs=cs_pin, rst=rst_pin, baudrate=BAUDRATE)
display.init()
display.fill(WHITE)

We have a helper function next for decoding 16-bit bitmaps into the correct bit structure. Even though we are using a 24-bit bmp in this example, if you would like to replace the image with one of your own and it happens to be a 16-bit bitmap, it will still work.

Download: file
def convert_555_to_565(rgb):
    return (rgb & 0x7FE0) << 1 | 0x20 | rgb & 0x001F

After that, we define a BMP class that will read the headers, decode the bitmap, and draw it to the screen. It starts off by initializing all of the values to 0 or None.

Download: file
class BMP(object):
    def __init__(self, filename):
        self.filename = filename
        self.colors = None
        self.data = 0
        self.data_size = 0
        self.bpp = 0
        self.width = 0
        self.height = 0
        self.read_header()

Then it goes through and reads the important information in the header at specific places in the file. In this case, the bytes are in little endian format which means the bytes are arranged with starting with the least significant byte values are at the smallest address. If you are interested, you can read more about endianness, on Wikipedia.

Download: file
    def read_header(self):
        if self.colors:
            return
        with open(self.filename, 'rb') as f:
            f.seek(10)
            self.data = int.from_bytes(f.read(4), 'little')
            f.seek(18)
            self.width = int.from_bytes(f.read(4), 'little')
            self.height = int.from_bytes(f.read(4), 'little')
            f.seek(28)
            self.bpp = int.from_bytes(f.read(2), 'little')
            f.seek(34)
            self.data_size = int.from_bytes(f.read(4), 'little')
            f.seek(46)
            self.colors = int.from_bytes(f.read(4), 'little')

In the Draw function we read the appropriate amount of data depending on the file encoding, make sure it's in a format that the display understands, and push it to the display. Because the data is pushed directly out to the RA8875, the amount of memory required is pretty minimal.

Download: file
    def draw(self, disp, x=0, y=0):
        print("{:d}x{:d} image".format(self.width, self.height))
        print("{:d}-bit encoding detected".format(self.bpp))
        line = 0
        line_size = self.width * (self.bpp//8)
        if line_size % 4 != 0:
            line_size += (4 - line_size % 4)
        current_line_data = b''
        with open(self.filename, 'rb') as f:
            f.seek(self.data)
            disp.set_window(x, y, self.width, self.height)
            for line in range(self.height):
                current_line_data = b''
                line_data = f.read(line_size)
                for i in range(0, line_size, self.bpp//8):
                    if (line_size-i) < self.bpp//8:
                        break
                    if self.bpp == 16:
                        color = convert_555_to_565(line_data[i] | line_data[i+1] << 8)
                    if self.bpp == 24 or self.bpp == 32:
                        color = color565(line_data[i+2], line_data[i+1], line_data[i])
                    current_line_data = current_line_data + struct.pack(">H", color)
                disp.setxy(x, self.height - line + y)
                disp.push_pixels(current_line_data)
            disp.set_window(0, 0, disp.width, disp.height)

Finally, we declare our class, figure out the center of the screen and draw the bitmap. This allows it to display on both the larger and smaller screens.

Download: file
bitmap = BMP("/ra8875_blinka.bmp")
x_position = (display.width // 2) - (bitmap.width // 2)
y_position = (display.height // 2) - (bitmap.height // 2)
bitmap.draw(display, x_position, y_position)
This guide was first published on Mar 19, 2019. It was last updated on Mar 19, 2019.
This page (CircuitPython Code) was last updated on Jul 03, 2020.