From 46f2faba5e594af56c3c1c7872b4dee892de89e7 Mon Sep 17 00:00:00 2001 From: DJDevon3 <49322231+DJDevon3@users.noreply.github.com> Date: Mon, 29 Apr 2024 01:32:52 -0400 Subject: [PATCH 1/6] BMP speed improvement Move class from example to library. --- adafruit_ra8875/ra8875.py | 93 ++++++++++++++++++++++++++++++++++++++ examples/ra8875_bmptest.py | 67 +-------------------------- 2 files changed, 95 insertions(+), 65 deletions(-) diff --git a/adafruit_ra8875/ra8875.py b/adafruit_ra8875/ra8875.py index 381b8b1..b21110a 100755 --- a/adafruit_ra8875/ra8875.py +++ b/adafruit_ra8875/ra8875.py @@ -45,6 +45,7 @@ __repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_RA8875.git" +# pylint: disable=too-many-lines # pylint: disable-msg=invalid-name def color565(r: int, g: int = 0, b: int = 0) -> int: """Convert red, green and blue values (0-255) into a 16-bit 565 encoding.""" @@ -983,3 +984,95 @@ def _ellipse_helper( self._wait_poll(reg.ELLIPSE, reg.ELLIPSE_STATUS) # pylint: enable-msg=invalid-name,too-many-arguments + + +class BMP: + """ + Optimized with ChatGPT by DJDevon3 + https://chat.openai.com/share/57ee2bb5-33ba-4538-a4b7-ec3dea8ea5c7 + + Draw Bitmap Helper Class (not hardware accelerated) + + :param str: filename BMP filename + :param int colors: BMP color data + :param int data: BMP data + :param int data_size: BMP data size + :param int bpp: BMP bit depth data + :param int width: BMP width + :param int height: BMP height + :param int read_header: BMP read header function + + """ + + def __init__(self, filename): + self.filename = filename + self.colors = None + self.data = None + self.data_size = 0 + self.bpp = 0 + self.width = 0 + self.height = 0 + self.read_header() + + def read_header(self): + """Read file header data""" + if self.colors: + return + with open(self.filename, "rb") as bmp_file: + bmp_file.seek(10) + self.data = int.from_bytes(bmp_file.read(4), "little") + bmp_file.seek(18) + self.width = int.from_bytes(bmp_file.read(4), "little") + self.height = int.from_bytes(bmp_file.read(4), "little") + bmp_file.seek(28) + self.bpp = int.from_bytes(bmp_file.read(2), "little") + bmp_file.seek(34) + self.data_size = int.from_bytes(bmp_file.read(4), "little") + bmp_file.seek(46) + self.colors = int.from_bytes(bmp_file.read(4), "little") + + def draw(self, disp, x=0, y=0): + """Draw BMP""" + print("{:d}x{:d} image".format(self.width, self.height)) + print("{:d}-bit encoding detected".format(self.bpp)) + line_size = self.width * (self.bpp // 8) + if line_size % 4 != 0: + line_size += 4 - line_size % 4 + + with open(self.filename, "rb") as bmp_file: + bmp_file.seek(self.data) + pixel_data = bmp_file.read() + + disp.set_window(x, y, self.width, self.height) + for line in range(self.height): + current_line_data = b"" + line_start = line * line_size + line_end = line_start + line_size + for i in range(line_start, line_end, self.bpp // 8): + if (line_end - i) < self.bpp // 8: + break + if self.bpp == 16: + color = self.convert_555_to_565( + pixel_data[i] | pixel_data[i + 1] << 8 + ) + if self.bpp in (24, 32): + color = self.color565( + pixel_data[i + 2], pixel_data[i + 1], pixel_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) + + @staticmethod + def convert_555_to_565(color_555): + """Convert 16-bit color from 5-5-5 to 5-6-5 format""" + r = (color_555 & 0x1F) << 3 + g = ((color_555 >> 5) & 0x1F) << 2 + b = ((color_555 >> 10) & 0x1F) << 3 + return (r << 11) | (g << 5) | b + + @staticmethod + def color565(r, g, b): + """Convert 24-bit RGB color to 16-bit color (5-6-5 format)""" + return ((r & 0xF8) << 8) | ((g & 0xFC) << 3) | (b >> 3) diff --git a/examples/ra8875_bmptest.py b/examples/ra8875_bmptest.py index 4743591..f48514c 100755 --- a/examples/ra8875_bmptest.py +++ b/examples/ra8875_bmptest.py @@ -1,9 +1,7 @@ # SPDX-FileCopyrightText: 2021 ladyada for Adafruit Industries # SPDX-License-Identifier: MIT -# Quick bitmap test of RA8875 with Feather M4 -import struct - +# Quick bitmap test of RA8875 with FeatherS3 import busio import digitalio import board @@ -28,68 +26,7 @@ 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") +bitmap = ra8875.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) From d303657f52037f9f0fe86c1b6a5859732c74122e Mon Sep 17 00:00:00 2001 From: DJDevon3 <49322231+DJDevon3@users.noreply.github.com> Date: Wed, 1 May 2024 14:48:40 -0400 Subject: [PATCH 2/6] corrected Blue & Green RGB values --- examples/ra8875_simpletest.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/ra8875_simpletest.py b/examples/ra8875_simpletest.py index 8270c71..837486b 100644 --- a/examples/ra8875_simpletest.py +++ b/examples/ra8875_simpletest.py @@ -12,8 +12,8 @@ BLACK = color565(0, 0, 0) RED = color565(255, 0, 0) -BLUE = color565(0, 255, 0) -GREEN = color565(0, 0, 255) +GREEN = color565(0, 255, 0) +BLUE = color565(0, 0, 255) YELLOW = color565(255, 255, 0) CYAN = color565(0, 255, 255) MAGENTA = color565(255, 0, 255) From 437c77c41b639e6f9999cc0687da8ca915f3f81b Mon Sep 17 00:00:00 2001 From: DJDevon3 <49322231+DJDevon3@users.noreply.github.com> Date: Thu, 2 May 2024 06:26:48 -0400 Subject: [PATCH 3/6] moved BMP class to separate file The startup time isn't as fast but the draw time is. Allows BMP class and other file types in the future to be expanded upon more easily. Updated BMPtest example with the new imports and function calls. --- adafruit_ra8875/bmp.py | 101 +++++++++++++++++++++++++++++++++++++ adafruit_ra8875/ra8875.py | 97 +---------------------------------- examples/ra8875_bmptest.py | 11 ++-- 3 files changed, 108 insertions(+), 101 deletions(-) create mode 100644 adafruit_ra8875/bmp.py diff --git a/adafruit_ra8875/bmp.py b/adafruit_ra8875/bmp.py new file mode 100644 index 0000000..4c95b7f --- /dev/null +++ b/adafruit_ra8875/bmp.py @@ -0,0 +1,101 @@ +# SPDX-FileCopyrightText: 2021 ladyada for Adafruit Industries +# SPDX-License-Identifier: MIT +# 2024 Optimized with ChatGPT by DJDevon3 +# https://chat.openai.com/share/57ee2bb5-33ba-4538-a4b7-ec3dea8ea5c7 +""" Raw Bitmap Helper Class (not hardware accelerated)""" + +import struct + + +class BMP: + """ + Raw Bitmap Helper Class (not hardware accelerated) + + :param str: filename BMP filename + :param int colors: BMP color data + :param int data: BMP data + :param int data_size: BMP data size + :param int bpp: BMP bit depth data + :param int width: BMP width + :param int height: BMP height + :param int read_header: BMP read header function + + """ + + def __init__(self, filename, debug: bool = False): + self.filename = filename + self.colors = None + self.data = None + self.data_size = 0 + self.bpp = 0 + self.width = 0 + self.height = 0 + self.debug = debug # Store debug mode + self.read_header() + + def read_header(self): + """Read file header data""" + if self.colors: + return + with open(self.filename, "rb") as bmp_file: + header_data = bmp_file.read( + 54 + ) # Read the entire BMP header (assuming it's 54 bytes) + self.data = int.from_bytes(header_data[10:14], "little") + self.width = int.from_bytes(header_data[18:22], "little") + self.height = int.from_bytes(header_data[22:26], "little") + self.bpp = int.from_bytes(header_data[28:30], "little") + self.data_size = int.from_bytes(header_data[34:38], "little") + self.colors = int.from_bytes(header_data[46:50], "little") + if self.debug: # Check debug mode + print(f"Header Hex Dump: {header_data}") + print(f"Header Data: {self.data}") + print(f"Header Width: {self.width}") + print(f"Header Height: {self.height}") + print(f"Header BPP: {self.bpp}") + print(f"Header Size: {self.data_size}") + print(f"Header Colors: {self.colors}") + + def draw_bmp(self, disp, x: int = 0, y: int = 0, chunk_size: int = 1): + """Draw BMP""" + if self.debug: # Check debug mode + print(f"{self.width}x{self.height} image") + print(f"{self.bpp}-bit encoding detected") + + with open(self.filename, "rb") as bmp_file: + bmp_file.seek(self.data) + line_data = bmp_file.read() + + for start_line in range(0, self.height, chunk_size): + end_line = min(start_line + chunk_size, self.height) + current_line_data = b"" + for line in range(start_line, end_line): + line_start = line * self.width * (self.bpp // 8) + line_end = line_start + self.width * (self.bpp // 8) + for i in range(line_start, line_end, self.bpp // 8): + if (line_end - i) < self.bpp // 8: + break + if self.bpp == 16: + color = self.convert_555_to_565( + line_data[i] | line_data[i + 1] << 8 + ) + elif self.bpp in (24, 32): + color = self.color565( + line_data[i + 2], line_data[i + 1], line_data[i] + ) + current_line_data += struct.pack(">H", color) + disp.setxy(x, self.height - end_line + y) + disp.push_pixels(current_line_data) + + @staticmethod + def convert_555_to_565(color_555): + """Convert 16-bit color from 5-5-5 to 5-6-5 format""" + r = (color_555 & 0x1F) << 3 + g = ((color_555 >> 5) & 0x1F) << 2 + b = ((color_555 >> 10) & 0x1F) << 3 + return (r << 11) | (g << 5) | b + + @staticmethod + def color565(r, g, b): + """Convert 24-bit RGB color to 16-bit color (5-6-5 format)""" + return ((r & 0xF8) << 8) | ((g & 0xFC) << 3) | (b >> 3) diff --git a/adafruit_ra8875/ra8875.py b/adafruit_ra8875/ra8875.py index b21110a..cb5bc9e 100755 --- a/adafruit_ra8875/ra8875.py +++ b/adafruit_ra8875/ra8875.py @@ -32,7 +32,7 @@ from digitalio import Direction from adafruit_bus_device import spi_device -import adafruit_ra8875.registers as reg +from adafruit_ra8875 import registers as reg try: from typing import Optional, Tuple, Union @@ -45,7 +45,6 @@ __repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_RA8875.git" -# pylint: disable=too-many-lines # pylint: disable-msg=invalid-name def color565(r: int, g: int = 0, b: int = 0) -> int: """Convert red, green and blue values (0-255) into a 16-bit 565 encoding.""" @@ -982,97 +981,3 @@ def _ellipse_helper( # Draw it self._write_reg(reg.ELLIPSE, 0xC0 if filled else 0x80) self._wait_poll(reg.ELLIPSE, reg.ELLIPSE_STATUS) - - # pylint: enable-msg=invalid-name,too-many-arguments - - -class BMP: - """ - Optimized with ChatGPT by DJDevon3 - https://chat.openai.com/share/57ee2bb5-33ba-4538-a4b7-ec3dea8ea5c7 - - Draw Bitmap Helper Class (not hardware accelerated) - - :param str: filename BMP filename - :param int colors: BMP color data - :param int data: BMP data - :param int data_size: BMP data size - :param int bpp: BMP bit depth data - :param int width: BMP width - :param int height: BMP height - :param int read_header: BMP read header function - - """ - - def __init__(self, filename): - self.filename = filename - self.colors = None - self.data = None - self.data_size = 0 - self.bpp = 0 - self.width = 0 - self.height = 0 - self.read_header() - - def read_header(self): - """Read file header data""" - if self.colors: - return - with open(self.filename, "rb") as bmp_file: - bmp_file.seek(10) - self.data = int.from_bytes(bmp_file.read(4), "little") - bmp_file.seek(18) - self.width = int.from_bytes(bmp_file.read(4), "little") - self.height = int.from_bytes(bmp_file.read(4), "little") - bmp_file.seek(28) - self.bpp = int.from_bytes(bmp_file.read(2), "little") - bmp_file.seek(34) - self.data_size = int.from_bytes(bmp_file.read(4), "little") - bmp_file.seek(46) - self.colors = int.from_bytes(bmp_file.read(4), "little") - - def draw(self, disp, x=0, y=0): - """Draw BMP""" - print("{:d}x{:d} image".format(self.width, self.height)) - print("{:d}-bit encoding detected".format(self.bpp)) - line_size = self.width * (self.bpp // 8) - if line_size % 4 != 0: - line_size += 4 - line_size % 4 - - with open(self.filename, "rb") as bmp_file: - bmp_file.seek(self.data) - pixel_data = bmp_file.read() - - disp.set_window(x, y, self.width, self.height) - for line in range(self.height): - current_line_data = b"" - line_start = line * line_size - line_end = line_start + line_size - for i in range(line_start, line_end, self.bpp // 8): - if (line_end - i) < self.bpp // 8: - break - if self.bpp == 16: - color = self.convert_555_to_565( - pixel_data[i] | pixel_data[i + 1] << 8 - ) - if self.bpp in (24, 32): - color = self.color565( - pixel_data[i + 2], pixel_data[i + 1], pixel_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) - - @staticmethod - def convert_555_to_565(color_555): - """Convert 16-bit color from 5-5-5 to 5-6-5 format""" - r = (color_555 & 0x1F) << 3 - g = ((color_555 >> 5) & 0x1F) << 2 - b = ((color_555 >> 10) & 0x1F) << 3 - return (r << 11) | (g << 5) | b - - @staticmethod - def color565(r, g, b): - """Convert 24-bit RGB color to 16-bit color (5-6-5 format)""" - return ((r & 0xF8) << 8) | ((g & 0xFC) << 3) | (b >> 3) diff --git a/examples/ra8875_bmptest.py b/examples/ra8875_bmptest.py index f48514c..f35f75e 100755 --- a/examples/ra8875_bmptest.py +++ b/examples/ra8875_bmptest.py @@ -7,16 +7,15 @@ import board from adafruit_ra8875 import ra8875 +from adafruit_ra8875.bmp import BMP 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 +BAUDRATE = 6000000 # Setup SPI bus using hardware SPI: spi = busio.SPI(clock=board.SCK, MOSI=board.MOSI, MISO=board.MISO) @@ -24,9 +23,11 @@ # Create and setup the RA8875 display: display = ra8875.RA8875(spi, cs=cs_pin, rst=rst_pin, baudrate=BAUDRATE) display.init() + +WHITE = color565(255, 255, 255) display.fill(WHITE) -bitmap = ra8875.BMP("/ra8875_blinka.bmp") +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) +bitmap.draw_bmp(display, x_position, y_position) From 090468c1bdf5c2e198e3ddb74f283eaa7df34c69 Mon Sep 17 00:00:00 2001 From: DJDevon3 <49322231+DJDevon3@users.noreply.github.com> Date: Mon, 6 May 2024 23:55:53 -0400 Subject: [PATCH 4/6] infinite pixel screensaver example For a screensaver --- examples/ra8875_screensaver.py | 46 ++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) create mode 100644 examples/ra8875_screensaver.py diff --git a/examples/ra8875_screensaver.py b/examples/ra8875_screensaver.py new file mode 100644 index 0000000..f72a04b --- /dev/null +++ b/examples/ra8875_screensaver.py @@ -0,0 +1,46 @@ +# SPDX-FileCopyrightText: 2024 DJDevon3 +# SPDX-License-Identifier: MIT + +# RA8875 Screen Saver example + +import random +import busio +import digitalio +import board + +from adafruit_ra8875 import ra8875 + +# 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() + +square_size = 32 +horizontal_squares = 25 +vertical_squares = 15 + +while True: + for row_index in range(vertical_squares): + for square_index in range(horizontal_squares): + x = square_index * square_size + y = row_index * square_size + + # Generate random RGB values for each square + r = random.randint(0, 255) + g = random.randint(0, 255) + b = random.randint(0, 255) + + # Convert RGB to color565 format + color565_value = ((r & 0xF8) << 8) | ((g & 0xFC) << 3) | (b >> 3) + + # Draw the square with the random color + display.fill_rect(x, y, square_size, square_size, color565_value) From 457bae4a69884af42845ad1dd62b00b1bb771ca5 Mon Sep 17 00:00:00 2001 From: DJDevon3 <49322231+DJDevon3@users.noreply.github.com> Date: Sun, 2 Jun 2024 08:32:25 -0400 Subject: [PATCH 5/6] add read single pixel function and colorchart test script ability to read a pixel color from the display at x,y coordinates and return as RGB values. Read data16 function makes it easier to chunk 16-bit color to/from register. Added some missing registers and register descriptions. --- adafruit_ra8875/ra8875.py | 299 +++++++++++++++++++----- adafruit_ra8875/registers.py | 289 ++++++++++++----------- examples/ra8875_color_chart.bmp | Bin 0 -> 101178 bytes examples/ra8875_color_chart.bmp.license | 2 + examples/ra8875_readsinglepixel_test.py | 159 +++++++++++++ 5 files changed, 553 insertions(+), 196 deletions(-) create mode 100644 examples/ra8875_color_chart.bmp create mode 100644 examples/ra8875_color_chart.bmp.license create mode 100644 examples/ra8875_readsinglepixel_test.py diff --git a/adafruit_ra8875/ra8875.py b/adafruit_ra8875/ra8875.py index cb5bc9e..f29975a 100755 --- a/adafruit_ra8875/ra8875.py +++ b/adafruit_ra8875/ra8875.py @@ -1,7 +1,7 @@ # SPDX-FileCopyrightText: 2019 Melissa LeBlanc-Williams for Adafruit Industries # # SPDX-License-Identifier: MIT - +# pylint: disable=too-many-lines """ `adafruit_ra8875.ra8875` ==================================================== @@ -58,6 +58,18 @@ def color565(r: int, g: int = 0, b: int = 0) -> int: # pylint: enable-msg=invalid-name +# pylint: disable-msg=invalid-name +def convert_555_to_565(color_555): + """Convert 16-bit color from 5-5-5 to 5-6-5 format""" + r = (color_555 & 0x1F) << 3 + g = ((color_555 >> 5) & 0x1F) << 2 + b = ((color_555 >> 10) & 0x1F) << 3 + return (r << 11) | (g << 5) | b + + +# pylint: enable-msg=invalid-name + + class RA8875_Device: """ Base Class for the Display. Contains all the low level stuff. As well @@ -112,6 +124,9 @@ def init(self, start_on: bool = True) -> None: :param bool start_on: (optional) If the display should start in an On State (default=True) """ + hw_init = True + release_displays = True + if self.width == 480 and self.height == 82: self.vert_offset = 190 @@ -137,40 +152,42 @@ def init(self, start_on: bool = True) -> None: self.pllinit() - self._write_reg(reg.SYSR, reg.SYSR_16BPP | reg.SYSR_MCU8) - self._write_reg(reg.PCSR, pixclk) - time.sleep(0.001) + if hw_init: # Bits Per Pixel: 16 | Bus: 8-Bit + self._write_reg(reg.SYSR, reg.SYSR_16BPP | reg.SYSR_MCU8) + self._write_reg(reg.PCSR, pixclk) + time.sleep(0.001) - # Horizontal settings registers - self._write_reg(reg.HDWR, self.width // 8 - 1) - self._write_reg(reg.HNDFTR, reg.HNDFTR_DE_HIGH) - self._write_reg(reg.HNDR, (hsync_nondisp - 2) // 8) - self._write_reg(reg.HSTR, hsync_start // 8 - 1) - self._write_reg(reg.HPWR, reg.HPWR_LOW + hsync_pw // 8 - 1) - - # Vertical settings registers - self._write_reg16(reg.VDHR0, self.height - 1 + self.vert_offset) - self._write_reg16(reg.VNDR0, vsync_nondisp - 1) - self._write_reg16(reg.VSTR0, vsync_start - 1) - self._write_reg(reg.VPWR, reg.VPWR_LOW + vsync_pw - 1) - - # Set active window X - self._write_reg16(reg.HSAW0, 0) - self._write_reg16(reg.HEAW0, self.width - 1) - - # Set active window Y - self._write_reg16(reg.VSAW0, self.vert_offset) - self._write_reg16(reg.VEAW0, self.height - 1 + self.vert_offset) - - # Clear the entire window - self._write_reg(reg.MCLR, reg.MCLR_START | reg.MCLR_FULL) - time.sleep(0.500) - - # Turn the display on, enable GPIO, and setup the backlight - self.turn_on(start_on) - self._gpiox(True) - self._pwm1_config(True, reg.PWM_CLK_DIV1024) - self.brightness(255) + if release_displays: # equivilent to displayio release_displays() + # Horizontal settings registers + self._write_reg(reg.HDWR, self.width // 8 - 1) + self._write_reg(reg.HNDFTR, reg.HNDFTR_DE_HIGH) + self._write_reg(reg.HNDR, (hsync_nondisp - 2) // 8) + self._write_reg(reg.HSTR, hsync_start // 8 - 1) + self._write_reg(reg.HPWR, reg.HPWR_LOW + hsync_pw // 8 - 1) + + # Vertical settings registers + self._write_reg16(reg.VDHR0, self.height - 1 + self.vert_offset) + self._write_reg16(reg.VNDR0, vsync_nondisp - 1) + self._write_reg16(reg.VSTR0, vsync_start - 1) + self._write_reg(reg.VPWR, reg.VPWR_LOW + vsync_pw - 1) + + # Set active window X + self._write_reg16(reg.HSAW0, 0) + self._write_reg16(reg.HEAW0, self.width - 1) + + # Set active window Y + self._write_reg16(reg.VSAW0, self.vert_offset) + self._write_reg16(reg.VEAW0, self.height - 1 + self.vert_offset) + + # Clear the entire window + self._write_reg(reg.MCLR, reg.MCLR_START | reg.MCLR_FULL) + time.sleep(0.500) + + # Turn the display on, enable GPIO, and setup the backlight + self.turn_on(start_on) + self._gpiox(True) + self._pwm1_config(True, reg.PWM_CLK_DIV1024) + self.brightness(255) def pllinit(self) -> None: """Init the Controller PLL""" @@ -179,7 +196,9 @@ def pllinit(self) -> None: self._write_reg(reg.PLLC2, reg.PLLC2_DIV4) time.sleep(0.001) - def _write_reg(self, cmd: int, data: int, raw: bool = False) -> None: + def _write_reg( + self, cmd: int, data: int, raw: bool = False, debug: bool = False + ) -> None: """ Select a Register and write a byte or push raw data out @@ -188,10 +207,14 @@ def _write_reg(self, cmd: int, data: int, raw: bool = False) -> None: :type data: byte or bytearray :param bool raw: (optional) Is the data a raw bytearray (default=False) """ + if debug: + print(f"_write_reg: cmd={cmd}, data={data}, raw={raw}") self._write_cmd(cmd) self._write_data(data, raw) - def _write_reg16(self, cmd: int, data: Union[int, bytearray]) -> None: + def _write_reg16( + self, cmd: int, data: Union[int, bytearray], debug: bool = False + ) -> None: """ Select a Register and write 2 bytes or push raw data out @@ -199,22 +222,26 @@ def _write_reg16(self, cmd: int, data: Union[int, bytearray]) -> None: :param data: The byte to write to the register :type data: byte or bytearray """ + if debug: + print(f"_write_reg16: cmd={cmd}, data={data}") self._write_cmd(cmd) self._write_data(data) self._write_cmd(cmd + 1) self._write_data(data >> 8) - def _write_cmd(self, cmd: int) -> None: + def _write_cmd(self, cmd: int, debug: bool = False) -> None: """ Select a Register/Command :param byte cmd: The register to select """ + if debug: + print(f"_write_cmd: cmd={cmd}") with self.spi_device as spi: spi.write(reg.CMDWR) # pylint: disable=no-member spi.write(bytearray([cmd & 0xFF])) # pylint: disable=no-member - def _write_data(self, data: int, raw: bool = False) -> None: + def _write_data(self, data: int, raw: bool = False, debug: bool = False) -> None: """ Write a byte or push raw data out @@ -222,6 +249,8 @@ def _write_data(self, data: int, raw: bool = False) -> None: :type data: byte or bytearray :param bool raw: (optional) Is the data a raw bytearray (default=False) """ + if debug: + print(f"_write_data: data={data}, raw={raw}") with self.spi_device as spi: spi.write(reg.DATWR) # pylint: disable=no-member if raw and isinstance(data, str): @@ -230,7 +259,7 @@ def _write_data(self, data: int, raw: bool = False) -> None: data if raw else bytearray([data & 0xFF]) ) # pylint: disable=no-member - def _read_reg(self, cmd: int) -> int: + def _read_reg(self, cmd: int, debug: bool = False) -> int: """ Select a Register and read a byte @@ -238,10 +267,12 @@ def _read_reg(self, cmd: int) -> int: :return: The results of the register :rtype: byte """ + if debug: + print(f"_read_reg: cmd={cmd}") self._write_cmd(cmd) return self._read_data() - def _read_data(self) -> int: + def _read_data(self, debug: bool = False) -> int: """ Read the data of the previously selected register @@ -249,11 +280,44 @@ def _read_data(self) -> int: :rtype: byte """ data = bytearray(1) + if debug: + print(f"_read_data: data={data}") with self.spi_device as spi: spi.write(reg.DATRD) # pylint: disable=no-member spi.readinto(data) # pylint: disable=no-member return struct.unpack(">B", data)[0] + def _read_data16(self, debug: bool = False) -> int: + """ + Read 16 bits of data from the previously selected register + + :return: The 16-bit data read from the register + :rtype: int + """ + # Allocate space for 2 bytes + data = bytearray(2) + + if debug: + print("_read_data16: Reading 16 bits of data") + + with self.spi_device as spi: + spi.write(reg.DATRD) # Write the data read command + spi.readinto(data) # Read 2 bytes of data + if debug: + print( + f"_read_data16: (data[0]: {data[0] & 0xFF:08b} " + + f"{(data[0] >> 8) & 0xFF:08b} data[1]: {data[1] & 0xFF:08b} " + + f"{(data[1] >> 8) & 0xFF:08b})" + ) + + # Combine the two bytes into a single 16-bit integer + result = (data[0] << 8) | data[1] + + if debug: + print(f"_read_data16: Read data: {result:#06x} (Binary: {result:016b})") + + return result + def _wait_poll(self, register: int, mask: int) -> bool: """ Keep checking a status bit and wait for an operation to complete. @@ -528,15 +592,20 @@ def set_bgcolor(self, color: int) -> None: self._write_reg(0x61, (color & 0x07E0) >> 5) self._write_reg(0x62, (color & 0x001F)) - def set_color(self, color: int) -> None: + def set_color(self, color: int, debug: bool = False) -> None: """ Set the foreground color for graphics/text :param int color: The of the text or graphics """ - self._write_reg(0x63, (color & 0xF800) >> 11) - self._write_reg(0x64, (color & 0x07E0) >> 5) - self._write_reg(0x65, (color & 0x001F)) + r = (color & 0xF800) >> 11 + g = (color & 0x07E0) >> 5 + b = color & 0x001F + if debug: + print(f"set_color: {color} (R: {r}, G: {g}, B: {b})") + self._write_reg(0x63, r) + self._write_reg(0x64, g) + self._write_reg(0x65, b) # pylint: disable-msg=invalid-name def pixel(self, x: int, y: int, color: int) -> None: @@ -562,29 +631,79 @@ def push_pixels(self, pixel_data: bytearray) -> None: self._write_reg(reg.MRWC, pixel_data, True) # pylint: disable-msg=invalid-name,too-many-arguments - def set_window(self, x: int, y: int, width: int, height: int) -> None: + def set_window( + self, + x1: int, + y1: int, + win1_width: int, + win1_height: int, + color: int, + filled: bool = True, + debug: bool = False, + ) -> None: """ Set an Active Drawing Window, which can be used in conjuntion with push_pixels for faster drawing - :param int x: The X coordinate of the left side of the window - :param int y: The Y coordinate of the top side of the window + :param int x1: The X coordinate of the left side of the window + :param int y1: The Y coordinate of the top side of the window :param int width: The width of the window :param int height: The height of the window + :param Tuple[int, int, int] bg_color: The background color of the window as an RGB tuple """ - if x + width >= self.width: - width = self.width - x - if y + height >= self.height: - height = self.height - y - # X - self._write_reg16(reg.HSAW0, x) - self._write_reg16(reg.HEAW0, x + width) - # Y - self._write_reg16(reg.VSAW0, y) - self._write_reg16(reg.VEAW0, y + height) + if x1 + win1_width >= self.width: + win1_width = self.width - x1 + if y1 + win1_height >= self.height: + win1_height = self.height - y1 + + self._gfx_mode() + + # Set window coordinates + self._write_reg16(reg.HSAW0, x1) + self._write_reg16(reg.HEAW0, x1 + win1_width) + self._write_reg16(reg.VSAW0, y1) + self._write_reg16(reg.VEAW0, y1 + win1_height) + + # Set X and Y + self._write_reg16(0x91, x1) + self._write_reg16(0x93, y1 + self.vert_offset) + + # Set Width and Height + self._write_reg16(0x95, win1_width) + self._write_reg16(0x97, win1_height + self.vert_offset) + + self.set_color(color) + if debug: + print(f"Color: {color}") + + # Draw it + self._write_reg(reg.DCR, 0xB0 if filled else 0x90) + self._wait_poll(reg.DCR, reg.DCR_LNSQTR_STATUS) # pylint: enable-msg=invalid-name,too-many-arguments + def lighten_mode(self): + """Transparency Lighten""" + self._gfx_mode() + self._write_reg(0x52, 0b01010000, debug=False) + + # pylint: disable-msg=invalid-name,too-many-arguments + def draw_cursor(self, x, y, inside_color, outline_color, debug: bool = False): + """Draw 2-color crosshair cursor at x,y position""" + # Draw the outline of the cursor + for i in range(-4, 5): + self.pixel(x + i, y - 4, outline_color) # Top line + self.pixel(x + i, y + 4, outline_color) # Bottom line + self.pixel(x - 4, y + i, outline_color) # Left line + self.pixel(x + 4, y + i, outline_color) # Right line + + # Draw the inside of the cursor + for i in range(-3, 4): + self.pixel(x + i, y, inside_color) # Horizontal line + self.pixel(x, y + i, inside_color) # Vertical line + if debug: + print(f"Drawing Cursor at: {x},{y}") + class RA8875(RA8875Display): """ @@ -593,8 +712,52 @@ class RA8875(RA8875Display): currently 800x480 and 480x272. """ + def read_single_pixel(self, x: int, y: int, debug: bool = True) -> int: + """ + Read the color of a single pixel from layer 0 of the display at x,y coordinates. + + :param int x: X coordinate of the pixel. + :param int y: Y coordinate of the pixel. + :param bool debug: Flag to enable debug printing. + :return: Color of the pixel in RGB format (24-bit). + :rtype: int + """ + # Ensure x and y are within display bounds + if not (0 <= x < self.width and 0 <= y < self.height): + raise ValueError( + f"Coordinates ({x}, {y}) out of bounds for display size {self.width}x{self.height}" + ) + if debug: + print(f"Reading pixel at ({x}, {y})") + + # Set graphics mode + self._gfx_mode() + # Set read cursor position for layer0 (800x480 16-bit mode) + self._write_reg16(reg.RCURH0, x, debug=False) + self._write_reg16(reg.RCURV0, y + self.vert_offset, debug=False) + # Set Memory Read/Write Control + self._read_reg(reg.MRWC) + # Read the color data + dummy = self._read_data16(debug=False) # Dummy read + msb = self._read_data16(debug=False) # MSB read + + # Extract color components from MSB + green = (msb >> 8) & 0xFF # Extracting 8 bits of green component + blue = (msb >> 3) & 0xFF # Extracting 8 bits of blue component + red = (msb << 3) & 0xFF # Extracting 8 bits of red component + if debug: + print(f"Dummy: {dummy} MSB: {msb}") + print(f"Extracted Colors: {red}, {green}, {blue}") + print( + f"Binary values: Red: {red:08b}, Green: {green:08b}, Blue: {blue:08b}" + ) + + return red, green, blue + # pylint: disable-msg=invalid-name,too-many-arguments - def rect(self, x: int, y: int, width: int, height: int, color: int) -> None: + def rect( + self, x: int, y: int, width: int, height: int, color: int, filled: bool = False + ) -> None: """ Draw a rectangle (HW Accelerated) @@ -604,9 +767,11 @@ def rect(self, x: int, y: int, width: int, height: int, color: int) -> None: :param int height: The height of the rectangle :param int color: The color of the rectangle """ - self._rect_helper(x, y, x + width - 1, y + height - 1, color, False) + self._rect_helper(x, y, x + width - 1, y + height - 1, color, filled=filled) - def fill_rect(self, x: int, y: int, width: int, height: int, color: int) -> None: + def fill_rect( + self, x: int, y: int, width: int, height: int, color: int, debug: bool = False + ) -> None: """ Draw a filled rectangle (HW Accelerated) @@ -616,6 +781,8 @@ def fill_rect(self, x: int, y: int, width: int, height: int, color: int) -> None :param int height: The height of the rectangle :param int color: The color of the rectangle """ + if debug: + print(f"fill_rect color: {color}") self._rect_helper(x, y, x + width - 1, y + height - 1, color, True) def fill(self, color: int) -> None: @@ -882,7 +1049,14 @@ def _circle_helper( self._wait_poll(reg.DCR, reg.DCR_CIRC_STATUS) def _rect_helper( - self, x1: int, y1: int, x2: int, y2: int, color: int, filled: bool + self, + x1: int, + y1: int, + x2: int, + y2: int, + color: int, + filled: bool, + debug: bool = False, ) -> None: """General Rectangle Drawing Helper""" self._gfx_mode() @@ -894,7 +1068,8 @@ def _rect_helper( # Set Width and Height self._write_reg16(0x95, x2) self._write_reg16(0x97, y2 + self.vert_offset) - + if debug: + print(f"_rect_helper: color={color}") self.set_color(color) # Draw it diff --git a/adafruit_ra8875/registers.py b/adafruit_ra8875/registers.py index 0d35ce7..a6b2f3c 100755 --- a/adafruit_ra8875/registers.py +++ b/adafruit_ra8875/registers.py @@ -18,137 +18,158 @@ CMDRD = b"\xC0" # Status Read # Registers and Bits -PWRR = 0x01 -PWRR_DISPON = 0x80 -PWRR_DISPOFF = 0x00 -PWRR_SLEEP = 0x02 -PWRR_NORMAL = 0x00 -PWRR_SOFTRESET = 0x01 -MRWC = 0x02 -GPIOX = 0xC7 - -PLLC1 = 0x88 -PLLC1_PLLDIV1 = 0x00 - -PLLC2 = 0x89 -PLLC2_DIV4 = 0x02 - -SYSR = 0x10 -SYSR_8BPP = 0x00 -SYSR_16BPP = 0x0C -SYSR_MCU8 = 0x00 -SYSR_MCU16 = 0x03 - -PCSR = 0x04 -PCSR_PDATR = 0x00 -PCSR_PDATL = 0x80 -PCSR_CLK = 0x00 -PCSR_2CLK = 0x01 -PCSR_4CLK = 0x02 -PCSR_8CLK = 0x03 - -HDWR = 0x14 - -HNDFTR = 0x15 -HNDFTR_DE_HIGH = 0x00 -HNDFTR_DE_LOW = 0x80 - -HNDR = 0x16 -HSTR = 0x17 -HPWR = 0x18 -HPWR_LOW = 0x00 -HPWR_HIGH = 0x80 - -VDHR0 = 0x19 -VNDR0 = 0x1B -VSTR0 = 0x1D -VPWR = 0x1F -VPWR_LOW = 0x00 -VPWR_HIGH = 0x80 - -FNCR0 = 0x21 -FNCR1 = 0x22 - -HSAW0 = 0x30 -VSAW0 = 0x32 - -HEAW0 = 0x34 -VEAW0 = 0x36 - -MCLR = 0x8E -MCLR_START = 0x80 -MCLR_STOP = 0x00 -MCLR_READSTATUS = 0x80 -MCLR_FULL = 0x00 -MCLR_ACTIVE = 0x40 - -DCR = 0x90 -DCR_LNSQTR_START = 0x80 -DCR_LNSQTR_STOP = 0x00 -DCR_LNSQTR_STATUS = 0x80 -DCR_CIRC_START = 0x40 -DCR_CIRC_STATUS = 0x40 -DCR_CIRC_STOP = 0x00 -DCR_FILL = 0x20 -DCR_NOFILL = 0x00 -DCR_DRAWLN = 0x00 -DCR_DRAWTRI = 0x01 -DCR_DRAWSQU = 0x10 - -ELLIPSE = 0xA0 -ELLIPSE_STATUS = 0x80 - -MWCR0 = 0x40 -MWCR0_GFXMODE = 0x00 -MWCR0_TXTMODE = 0x80 - -CURH0 = 0x46 -CURV0 = 0x48 - -P1CR = 0x8A -P1CR_ENABLE = 0x80 -P1CR_DISABLE = 0x00 -P1CR_CLKOUT = 0x10 -P1CR_PWMOUT = 0x00 - -P1DCR = 0x8B - -P2CR = 0x8C -P2CR_ENABLE = 0x80 -P2CR_DISABLE = 0x00 -P2CR_CLKOUT = 0x10 -P2CR_PWMOUT = 0x00 - -P2DCR = 0x8D -PWM_CLK_DIV1024 = 0x0A - -TPCR0 = 0x70 -TPCR0_ENABLE = 0x80 -TPCR0_DISABLE = 0x00 -TPCR0_WAIT_4096CLK = 0x30 -TPCR0_WAKEENABLE = 0x08 -TPCR0_WAKEDISABLE = 0x00 -TPCR0_ADCCLK_DIV4 = 0x02 -TPCR0_ADCCLK_DIV16 = 0x04 - -TPCR1 = 0x71 -TPCR1_AUTO = 0x00 -TPCR1_MANUAL = 0x40 -TPCR1_DEBOUNCE = 0x04 -TPCR1_NODEBOUNCE = 0x00 - -TPXH = 0x72 -TPYH = 0x73 -TPXYL = 0x74 - -INTC1 = 0xF0 -INTC1_KEY = 0x10 -INTC1_DMA = 0x08 -INTC1_TP = 0x04 -INTC1_BTE = 0x02 - -INTC2 = 0xF1 -INTC2_KEY = 0x10 -INTC2_DMA = 0x08 -INTC2_TP = 0x04 -INTC2_BTE = 0x02 +PWRR = 0x01 # Power Register +PWRR_DISPON = 0x80 # Display on +PWRR_DISPOFF = 0x00 # Display off +PWRR_SLEEP = 0x02 # Sleep mode +PWRR_NORMAL = 0x00 # Normal mode +PWRR_SOFTRESET = 0x01 # Software reset + +MRWC = 0x02 # Memory Read/Write Control + +GPIOX = 0xC7 # GPIOX + +PLLC1 = 0x88 # PLL Control 1 +PLLC1_PLLDIV1 = 0x00 # PLLDIV1 + +PLLC2 = 0x89 # PLL Control 2 +PLLC2_DIV4 = 0x02 # PLLDIV4 + +SYSR = 0x10 # System Configuration + +SYSR_8BPP = 0x00 # 8 bits per pixel +SYSR_16BPP = 0x0C # 16 bits per pixel +SYSR_MCU8 = 0x00 # MCU 8-bit bus +SYSR_MCU16 = 0x03 # MCU 16-bit bus + +PCSR = 0x04 # PWM and Clock Control + +PCSR_PDATR = 0x00 # PWM: Data from PWM pin +PCSR_PDATL = 0x80 # PWM: Data from PWM register +PCSR_CLK = 0x00 # PWM: System clock +PCSR_2CLK = 0x01 # PWM: 2 System clocks +PCSR_4CLK = 0x02 # PWM: 4 System clocks +PCSR_8CLK = 0x03 # PWM: 8 System clocks + +HDWR = 0x14 # Hardware Width + +HNDFTR = 0x15 # Non-Display Area Start + +HNDFTR_DE_HIGH = 0x00 # DE signal high +HNDFTR_DE_LOW = 0x80 # DE signal low + +HNDR = 0x16 # Non-Display Area End +HSTR = 0x17 # HSYNC Start +HPWR = 0x18 # HSYNC Pulse Width +HPWR_LOW = 0x00 # HSYNC Low +HPWR_HIGH = 0x80 # HSYNC High + +VDHR0 = 0x19 # Vertical Start +VNDR0 = 0x1B # Vertical End +VSTR0 = 0x1D # VSYNC Start + +VPWR = 0x1F # VSYNC Pulse Width +VPWR_LOW = 0x00 # VSYNC Low +VPWR_HIGH = 0x80 # VSYNC High + +FNCR0 = 0x21 # Font Control 0 +FNCR1 = 0x22 # Font Control 1 + +HSAW0 = 0x30 # Horizontal Start Point 0 of Active Window +HSAW1 = 0x31 # Horizontal Start Point 1 of Active Window +VSAW0 = 0x32 # Vertical Start Point 0 of Active Window +VSAW1 = 0x33 # Vertical Start Point 1 of Active Window + +HEAW0 = 0x34 # Horizontal End Point 0 of Active Window +HEAW1 = 0x35 # Horizontal End Point 1 of Active Window +VEAW0 = 0x36 # Vertical End Point of Active Window 0 +VEAW1 = 0x37 # Vertical End Point of Active Window 1 + +MCLR = 0x8E # Memory Clear Control + +MCLR_START = 0x80 # Start Clearing Memory +MCLR_STOP = 0x00 # Stop Clearing Memory +MCLR_READSTATUS = 0x80 # Read Status +MCLR_FULL = 0x00 # Full Clear +MCLR_ACTIVE = 0x40 # Clear Active + +DCR = 0x90 # Drawing Control +DCR_LNSQTR_START = 0x80 # Line / Square / Triangle Drawing Start +DCR_LNSQTR_STOP = 0x00 # Line / Square / Triangle Drawing Stop +DCR_LNSQTR_STATUS = 0x80 # Line / Square / Triangle Drawing Status +DCR_CIRC_START = 0x40 # Circle Drawing Start +DCR_CIRC_STATUS = 0x40 # Circle Drawing Status +DCR_CIRC_STOP = 0x00 # Circle Drawing Stop +DCR_FILL = 0x20 # Fill Shape +DCR_NOFILL = 0x00 # Do Not Fill Shape +DCR_DRAWLN = 0x00 # Draw Line +DCR_DRAWTRI = 0x01 # Draw Triangle +DCR_DRAWSQU = 0x10 # Draw Square + +ELLIPSE = 0xA0 # Ellipse Setup +ELLIPSE_STATUS = 0x80 # Ellipse Setup Status + +MWCR0 = 0x40 # Memory Write Control +MWCR0_GFXMODE = 0x00 # Graphics Mode +MWCR0_TXTMODE = 0x80 # Text Mode + +MRCD = 0x45 # Memory Read Cursor Direction + +CURH0 = 0x46 # Memory Write Cursor Horizontal Position Register 0 +CURH1 = 0x47 # Memory Write Cursor Horizontal Position Register 1 +CURV0 = 0x48 # Memory Write Cursor Vertical Position Register 0 +CURV1 = 0x49 # Memory Write Cursor Vertical Position Register 1 + +RCURH0 = 0x4A # Memory Read Cursor Horizontal Position Register 0 +RCURH1 = 0x4B # Memory Read Cursor Horizontal Position Register 1 +RCURV0 = 0x4C # Memory Read Cursor Vertical Position Register 0 +RCURV1 = 0x4D # Memory Read Cursor Vertical Position Register 1 + +P1CR = 0x8A # Pointer 1 Control Register +P1CR_ENABLE = 0x80 # Enable Pointer 1 +P1CR_DISABLE = 0x00 # Disable Pointer 1 +P1CR_CLKOUT = 0x10 # Clock out Pointer 1 +P1CR_PWMOUT = 0x00 # PWM out Pointer 1 + +P1DCR = 0x8B # Pointer 1 Default Color Register + +P2CR = 0x8C # Pointer 2 Control Register +P2CR_ENABLE = 0x80 # Enable Pointer 2 +P2CR_DISABLE = 0x00 # Disable Pointer 2 +P2CR_CLKOUT = 0x10 # Clock out Pointer 2 +P2CR_PWMOUT = 0x00 # PWM out Pointer 2 + +P2DCR = 0x8D # Pointer 2 Default Color Register + +PWM_CLK_DIV1024 = 0x0A # PWM Clock Divider + +TPCR0 = 0x70 # Touch Panel Control Register 0 +TPCR0_ENABLE = 0x80 # Enable Touch Panel +TPCR0_DISABLE = 0x00 # Disable Touch Panel +TPCR0_WAIT_4096CLK = 0x30 # Wait 4096 clocks +TPCR0_WAKEENABLE = 0x08 # Wake Enable +TPCR0_WAKEDISABLE = 0x00 # Wake Disable +TPCR0_ADCCLK_DIV4 = 0x02 # ADC Clock Divider 4 +TPCR0_ADCCLK_DIV16 = 0x04 # ADC Clock Divider 16 + +TPCR1 = 0x71 # Touch Panel Control Register 1 +TPCR1_AUTO = 0x00 # Automatic Mode +TPCR1_MANUAL = 0x40 # Manual Mode +TPCR1_DEBOUNCE = 0x04 # Debounce +TPCR1_NODEBOUNCE = 0x00 # No Debounce + +TPXH = 0x72 # Touch Panel X High Register +TPYH = 0x73 # Touch Panel Y High Register +TPXYL = 0x74 # Touch Panel XY Low Register + +INTC1 = 0xF0 # Interrupt Control Register 1 +INTC1_KEY = 0x10 # Interrupt: Key +INTC1_DMA = 0x08 # Interrupt: DMA +INTC1_TP = 0x04 # Interrupt: Touch Panel +INTC1_BTE = 0x02 # Interrupt: BTE + +INTC2 = 0xF1 # Interrupt Control Register 2 +INTC2_KEY = 0x10 # Interrupt: Key +INTC2_DMA = 0x08 # Interrupt: DMA +INTC2_TP = 0x04 # Interrupt: Touch Panel +INTC2_BTE = 0x02 # Interrupt: BTE diff --git a/examples/ra8875_color_chart.bmp b/examples/ra8875_color_chart.bmp new file mode 100644 index 0000000000000000000000000000000000000000..2b7744286edf7ac1be15aff8da2f10a22f05a686 GIT binary patch literal 101178 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 R@8!l#2Yuh6g~VZX{tq&y0Y3l$ literal 0 HcmV?d00001 diff --git a/examples/ra8875_color_chart.bmp.license b/examples/ra8875_color_chart.bmp.license new file mode 100644 index 0000000..58f84ca --- /dev/null +++ b/examples/ra8875_color_chart.bmp.license @@ -0,0 +1,2 @@ +# SPDX-FileCopyrightText: 2024 DJDevon3 +# SPDX-License-Identifier: MIT diff --git a/examples/ra8875_readsinglepixel_test.py b/examples/ra8875_readsinglepixel_test.py new file mode 100644 index 0000000..1555731 --- /dev/null +++ b/examples/ra8875_readsinglepixel_test.py @@ -0,0 +1,159 @@ +# SPDX-FileCopyrightText: 2024 DJDevon3 +# SPDX-License-Identifier: MIT +"""RA8875 Read Single Pixel example""" + +import time +import busio +import digitalio +import board + +from adafruit_ra8875 import ra8875 +from adafruit_ra8875.bmp import BMP +from adafruit_ra8875.ra8875 import color565 + +# 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() + +BLACK = color565(0, 0, 0) +RED = color565(255, 0, 0) +GREEN = color565(0, 255, 0) +BLUE = color565(0, 0, 255) +YELLOW = color565(255, 255, 0) +CYAN = color565(0, 255, 255) +MAGENTA = color565(255, 0, 255) +WHITE = color565(255, 255, 255) + +# Load the bitmap image +bitmap = BMP("/ra8875_color_chart.bmp") + +# Center BMP image on the display +x_position = (display.width // 2) - (bitmap.width // 2) +y_position = (display.height // 2) - (bitmap.height // 2) + +# Fill entire display background with white +display.fill(WHITE) +print("Filled display layer0 with white\n") + +# Draw BMP (bottom to top) +bitmap.draw_bmp(display, x_position, y_position) + +# Coordinates inside of a 53x53 red square in the bmp +X1 = 320 +Y1 = 190 +# Coordinates inside of a 53x53 blue square in the bmp +X2 = 370 +Y2 = 190 +# Coordinates inside of a 53x53 purple square in the bmp +X3 = 425 +Y3 = 190 +# Coordinates inside of a 53x53 yellow square in the bmp +X4 = 485 +Y4 = 190 +# Coordinates inside of a 53x53 green square in the bmp +X5 = 320 +Y5 = 240 +# Coordinates inside of a 53x53 cyan square in the bmp +X6 = 370 +Y6 = 240 +# Coordinates inside of a 53x53 white square in the bmp +X7 = 425 +Y7 = 240 +# Coordinates inside of a 53x53 black square in the bmp +X8 = 485 +Y8 = 240 +# Coordinates inside of a 53x53 yellow square in the bmp +X9 = 320 +Y9 = 290 +# Coordinates inside of a 53x53 red square in the bmp +X10 = 370 +Y10 = 290 +# Coordinates inside of a 53x53 green square in the bmp +X11 = 425 +Y11 = 290 +# Coordinates inside of a 53x53 blue square in the bmp +X12 = 485 +Y12 = 290 + +# List of color sampling coordinates +coordinates = [ + (X1, Y1), + (X2, Y2), + (X3, Y3), + (X4, Y4), + (X5, Y5), + (X6, Y6), + (X7, Y7), + (X8, Y8), + (X9, Y9), + (X10, Y10), + (X11, Y11), + (X12, Y12), +] + +# Giving them names makes it easier to spot errors +color_names = [ + "Red", + "Blue", + "Purple", + "Yellow", + "Green", + "Cyan", + "White", + "Black", + "Yellow", + "Red", + "Green", + "Blue", +] + +# Starting x,y for color rectangles to create +rect_coordinates = [ + (0, 0), + (53, 0), + (106, 0), + (159, 0), + (0, 53), + (53, 53), + (106, 53), + (159, 53), + (0, 106), + (53, 106), + (106, 106), + (159, 106), +] + +# Read every pixel at listed coordinates +# Returns colors as r,g,b +# Creates filled rectangles using r,g,b to confirm color sample +for i, (x, y) in enumerate(coordinates): + color = display.read_single_pixel(x, y) + print(f"color{i+1} at ({x},{y}): {color_names[i]} - {color}") + time.sleep(0.1) + rect_x, rect_y = rect_coordinates[i] + display.fill_rect(rect_x, rect_y, 53, 53, color565(color)) + +# Draws cross-hair to confirm sampled coordinates +# This can only happen after the sample is taken +display.draw_cursor(X1, Y1, RED, BLACK) +display.draw_cursor(X2, Y2, RED, BLACK) +display.draw_cursor(X3, Y3, RED, BLACK) +display.draw_cursor(X4, Y4, RED, BLACK) +display.draw_cursor(X5, Y5, RED, BLACK) +display.draw_cursor(X6, Y6, RED, BLACK) +display.draw_cursor(X7, Y7, RED, BLACK) +display.draw_cursor(X8, Y8, RED, BLACK) +display.draw_cursor(X9, Y9, RED, BLACK) +display.draw_cursor(X10, Y10, RED, BLACK) +display.draw_cursor(X11, Y11, RED, BLACK) +display.draw_cursor(X12, Y12, RED, BLACK) From 9f3b0c78b325958f66b544c2dedc179e06c36ba6 Mon Sep 17 00:00:00 2001 From: DJDevon3 <49322231+DJDevon3@users.noreply.github.com> Date: Sun, 2 Jun 2024 08:44:35 -0400 Subject: [PATCH 6/6] missed setting a debug false by default set debug default to false --- adafruit_ra8875/ra8875.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/adafruit_ra8875/ra8875.py b/adafruit_ra8875/ra8875.py index f29975a..78fc1b6 100755 --- a/adafruit_ra8875/ra8875.py +++ b/adafruit_ra8875/ra8875.py @@ -712,7 +712,7 @@ class RA8875(RA8875Display): currently 800x480 and 480x272. """ - def read_single_pixel(self, x: int, y: int, debug: bool = True) -> int: + def read_single_pixel(self, x: int, y: int, debug: bool = False) -> int: """ Read the color of a single pixel from layer 0 of the display at x,y coordinates.