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.
# SPDX-FileCopyrightText: 2021 ladyada for Adafruit Industries # SPDX-License-Identifier: MIT # Quick test of RA8875 with Feather M4 import time import busio import digitalio import board from adafruit_ra8875 import 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.
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.
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.
# 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.
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.
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.
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.
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.
# SPDX-FileCopyrightText: 2021 ladyada for Adafruit Industries # SPDX-License-Identifier: MIT # Quick bitmap test of RA8875 with Feather M4 import struct import busio import digitalio import board from adafruit_ra8875 import ra8875 from adafruit_ra8875.ra8875 import color565 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 in (24, 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.
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.
WHITE = color565(255, 255, 255)
Next, we initialize the screen and set the background to white.
# 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.
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.
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.
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.
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.
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)
Text editor powered by tinymce.