[{"element_type":"text","content":"\n        \u003ch2\u003eIntro to the RA8875 Driver Board for Circuit Python\u003c/h2\u003e\n\u003cp\u003eThe Adafruit RA8875 driver in Circuit Python\u003cstrong\u003e does not currently support displayio\u003c/strong\u003e.\u0026nbsp; You must use read/write registers with a barebones ra8875 graphics library. The current feature set and how it is used is\u0026nbsp;\u003cstrong\u003eonly for very advanced users\u003c/strong\u003e.\u0026nbsp;\u003c/p\u003e\n\u003cp\u003eYou can draw a bmp image and overlay text but you'll quickly find that's about all you can do with it.\u0026nbsp; There are only 2 examples provided and the driver board is unlike any other display device for Circuit Python. Any knowledge you have of displayio does not transfer over to this board; the RA8875 is unique.\u003c/p\u003e\n\u003cp\u003eThe interest of using an 800x480 bare display with Circuit Python is typically due to the sheer size of it but it should come with fair warning:\u0026nbsp; You must be capable of programming with circuit python from scratch without displayio.\u003c/p\u003e\n      ","metadata":{}},{"element_type":"user_image","content":"https://cdn-learn.adafruit.com/user_assets/assets/000/000/883/original/7_inch_TFT.jpg?1715097422","metadata":{"caption":"7\" SPI TFT (with touchscreen) from Adafruit"}},{"element_type":"text","content":"\n  \n        \u003ch3\u003eHardware:\u003c/h3\u003e\n\u003cul\u003e\n\u003cli\u003e\u003ca href=\"https://www.adafruit.com/product/2354\" target=\"_blank\"\u003e7\" 800x480 40-Pin SPI TFT\u003c/a\u003e\u003c/li\u003e\n\u003cli\u003e\u003ca href=\"https://www.adafruit.com/product/1590\" target=\"_blank\"\u003eAdafruit 40-Pin RA8875 Driver Board\u003c/a\u003e\u003c/li\u003e\n\u003cli\u003e\n\u003ca href=\"https://www.adafruit.com/product/5477\" target=\"_blank\"\u003eAdafruit ESP32-S3 Feather\u003c/a\u003e (N4R2)\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch3\u003eSoftware:\u003c/h3\u003e\n\u003cul\u003e\n\u003cli\u003e\n\u003ca href=\"https://circuitpython.org/\" target=\"_blank\"\u003eCircuit Python\u003c/a\u003e (current stable as of this note is 9.0.4)\u003c/li\u003e\n\u003cli\u003e\u003ca href=\"https://github.com/adafruit/Adafruit_CircuitPython_RA8875\" target=\"_blank\"\u003eadafruit_ra8875 library\u003c/a\u003e\u003c/li\u003e\n\u003c/ul\u003e\n      \n","metadata":{}},{"element_type":"user_image","content":"https://cdn-learn.adafruit.com/user_assets/assets/000/000/882/original/RA8875_to_Feather_Fritzing_Diagram.JPG?1715090163","metadata":{"caption":"RA8875 to ESP32-S3 Feather Fritzing Diagram"}},{"element_type":"text","content":"\n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n        \u003ch1\u003eWiring\u003c/h1\u003e\n\u003cp\u003eFrom left to right on the RA8875 driver board:\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003eVCC to USB/VBUS\u003c/li\u003e\n\u003cli\u003eGND to GND\u003c/li\u003e\n\u003cli\u003e3VO (unused)\u003c/li\u003e\n\u003cli\u003eLITE to 3V3\u003c/li\u003e\n\u003cli\u003eSCK to SCK\u003c/li\u003e\n\u003cli\u003eMISO to MISO\u003c/li\u003e\n\u003cli\u003eMOSI to MOSI\u003c/li\u003e\n\u003cli\u003eCS to D9\u003c/li\u003e\n\u003cli\u003eRST to D10\u003c/li\u003e\n\u003cli\u003eWAIT (unused)\u003c/li\u003e\n\u003cli\u003eINT to A5\u003c/li\u003e\n\u003cli\u003eY+ (unused)\u003c/li\u003e\n\u003cli\u003eY- (unused)\u003c/li\u003e\n\u003cli\u003eX- (unused)\u003c/li\u003e\n\u003cli\u003eX+ (unused)\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003eYou can power the RA8875 using either the 5V USB/VBUS pin or the 3V3 pin.\u0026nbsp; VCC is both 5V and 3.3V compliant. You can optionally choose to wire the LITE pin to a GPIO for use with PWM for backlight brightness control.\u0026nbsp; If you're not interested in backlight brightness control then wire LITE to 3V3 for full brightness all the time.\u003c/p\u003e\n\u003cp\u003eThere is an optional hardware WAIT pin that can be used to signal the beginning/end of a read/write cycle.\u0026nbsp; It communicates a busy signal that an uninterruptible process is taking place for the purpose of refining timings. There is a software version in the driver that is used as default which is why it is optional.\u003c/p\u003e\n\u003cp\u003eThe INT pin is for used touchscreen capability instead of the X,Y pins. It is not used in this project but it's good to hook up now.\u0026nbsp; The INT pin will need to be defined for any touch related projects.\u003c/p\u003e\n\u003cp\u003eThe RST pin is a hardware reset. This is optional as well as there is a software defined reset as default.\u0026nbsp; The RA8875 is automatically reset whenever the Feather is reset either with a soft-reload or hard reset.\u003c/p\u003e\n\u003cp\u003eThe CS pin is required and not optional. CS stands for Chip Select and basically provides the hardware address for the Feather to communicate with.\u003c/p\u003e\n\u003cp\u003eFor now only 2 pins need to be defined in software. The rest are implied with the use of busio such as CLK, MOSI, MISO.\u003c/p\u003e\n      \n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n","metadata":{}},{"element_type":"code","content":"import busio\nimport digitalio\nimport board\n\n# Configuration for CS and RST pins:\ncs_pin = digitalio.DigitalInOut(board.D9)\nrst_pin = digitalio.DigitalInOut(board.D10\n                                 \n# Setup SPI bus using hardware SPI:\nspi = busio.SPI(clock=board.SCK, MOSI=board.MOSI, MISO=board.MISO)","metadata":{"language":"python","linenums":false}},{"element_type":"text","content":"\n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n        \u003ch2\u003eThe Problem\u003c/h2\u003e\n\u003cp\u003eThe first hurdle; the current RA8875 library for Circuit Python cannot read a pixel from the display.\u0026nbsp; This is fairly important for buffering a section of the background to be replaced when you want to remove text.\u0026nbsp; Currently text cannot be removed only redrawn which ends up as a jumbled mess.\u0026nbsp; Most workarounds usually involve drawing a black rectangle which means your background has to be black... but what if you want a large image or multiple layers as a background? That's a problem.\u0026nbsp;\u003c/p\u003e\n\u003cp\u003eOK... so let's attempt to read a single pixel from the display, translate the color, and draw it.\u0026nbsp; That's the single most basic premise for being able to create layers. Without that basic functionality, more advanced functionality cannot exist yet. Yes the display has built-in methods to do this but they do not, and cannot, work as displayio does so you will be disappointed.\u0026nbsp; Also, they will not be compatible for a possible displayio future implementation so it's best not to rely on them right now. The library is still in too early in its development to even think about integrating them yet.\u003c/p\u003e\n\u003cp\u003eHere is a 24-bit color test BMP that I designed specifically for attempting to sample colors.\u0026nbsp; There are 12 53x53 squares of different colors in the image.\u003c/p\u003e\n      \n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n","metadata":{}},{"element_type":"user_image","content":"https://cdn-learn.adafruit.com/user_assets/assets/000/000/872/original/Color_test_chart2.jpg?1714998855","metadata":{"caption":"Color_test_chart2.bmp"}},{"element_type":"text","content":"\n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n        \u003ch2\u003eThe bits and bytes of binary color\u003c/h2\u003e\n\u003cp\u003eSince the RA8875 can only display a maximum of 16-bit color; the 24-bit image must be converted to 16-bit (color565).\u003c/p\u003e\n\u003cp\u003eThe RA8875 stores color information in its memory with 2 bytes (2 pairs of 8 bits).\u0026nbsp; Here is a binary representation of how it stores the color. 11111111 00000000\u0026nbsp; There are 8 bits in 1 byte.\u003c/p\u003e\n\u003cp\u003eHowever the RA8875 actually stores them in what is known as swapped color565.\u0026nbsp; Each first byte must be swapped with the 2nd. This is not some type of color conversion error. \u003cstrong\u003eThese are the direct reads from the memory addresses for the stored colors.\u003c/strong\u003e\u0026nbsp;\u003c/p\u003e\n      \n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n","metadata":{}},{"element_type":"code","content":"# Please note the first read is a dummy read and is to be discarded.\ndummy = self._read_data16(debug=False)  # Dummy read\nmsb = self._read_data16(debug=False)    # MSB read","metadata":{"language":"python","linenums":false}},{"element_type":"text","content":"\n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n        \u003cp\u003eThis is how the returned binary data from the read register will look with the dummy read and the actual color you want. Here is an example for the color blue (0,0,255)\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e_read_data16: (data[0]: 11100000 00000000 data[1]: 00000000 00000000) # dummy value\u003cbr\u003e_read_data16: (data[0]: 00011111 00000000 data[1]: 00000000 00000000) # real value\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003eThese represent 3 consecutive reads for the colors Red, Green, and then Blue (dummy values discarded)\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e00000000 11111000 \u003cbr\u003e11100000 00000111\u003cbr\u003e00011111 00000000\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003eI'm using an excellent \u003ca href=\"https://rgbcolorpicker.com/565\" target=\"_blank\"\u003eonline 565 RGB Color Picker\u003c/a\u003e tool to verify the binary values.\u003c/p\u003e\n\u003cp\u003eIf you attempt to convert these colors to 565 directly you'll notice that the colors are all wrong.\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003eRed is showing up as blue\u003c/li\u003e\n\u003cli\u003eGreen is showing up as red\u003c/li\u003e\n\u003cli\u003eBlue is showing up as green\u003c/li\u003e\n\u003c/ul\u003e\n      \n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n","metadata":{}},{"element_type":"user_image","content":"https://cdn-learn.adafruit.com/user_assets/assets/000/000/873/original/565_Swapped.jpg?1715002217","metadata":{"caption":""}},{"element_type":"text","content":"\n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n        \u003cp\u003eThis is because the RA8875 actually stores the 2 bytes of color values swapped.\u0026nbsp; This is known as swapped 565.\u003c/p\u003e\n\u003cp\u003eTo fix this all we have to do is swap byte 1 with byte 2 for each color.\u0026nbsp; We accomplish this by reading each 8-bit value separately and storing it in a variable.\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003edata[0] = 00011111\u003c/li\u003e\n\u003cli\u003edata[1] = 00000000\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003eWe then join them back together in the opposite order (without a space)\u003c/p\u003e\n\u003cp\u003e\u0026nbsp;data = data[1] data[0}\u003c/p\u003e\n\u003cp\u003eThey should now be a single 16-bit binary number (this represents blue) 0000000000011111 which can be correctly converted by a color565 operation. You can choose to use a swapped 565 instead but most people are used to working with R,G,B values (not B,R,G). This is part of the goal.\u003c/p\u003e\n      \n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n","metadata":{}},{"element_type":"user_image","content":"https://cdn-learn.adafruit.com/user_assets/assets/000/000/878/original/RA8875_8Bit_Registers.JPG?1715011623","metadata":{"caption":"RA8875 2-cycle 8-bit color register reads"}},{"element_type":"text","content":"\n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n        \u003cp\u003eThe 16-bit color is split into 2 separate 8-bit registers.\u0026nbsp; This is why reading it twice is necessary to extract the full 16-bit color.\u0026nbsp; After swapping the bytes and combining them they are now in the expected binary format for color565 (65K colors).\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e11111000 00000000 \u0026nbsp;= (255,0,0) Red\u003cbr\u003e00000111 11100000 \u0026nbsp;= (0,255,0) Green\u003cbr\u003e00000000 00011111 \u0026nbsp;= (0,0,255) Blue\u003c/li\u003e\n\u003c/ul\u003e\n      \n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n","metadata":{}},{"element_type":"user_image","content":"https://cdn-learn.adafruit.com/user_assets/assets/000/000/874/original/565_Binary.jpg?1715004709","metadata":{"caption":""}},{"element_type":"code","content":"# Extract color components from MSB\ngreen = (msb \u003e\u003e 8) \u0026 0xFF   # Extracting 8 bits of green component\nblue = (msb \u003e\u003e 3) \u0026 0xFF   # Extracting 8 bits of blue component\nred = (msb \u003c\u003c 3) \u0026 0xFF  # Extracting 8 bits of red component","metadata":{"language":"auto","linenums":false}},{"element_type":"text","content":"\n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n        \u003cp\u003eFiguring out how the RA8875 stores colors is just the first step to solving the goal of this playground note which is to read a pixel from the display and return it.\u0026nbsp; Now that we know how it stores colors we need to be able to retrieve a color at the exact x,y coordinates of the color test BMP.\u0026nbsp; We can accomplish this by using the RA8875 registers for setting an active read memory cursor at the location to be sampled.\u003c/p\u003e\n\u003cp\u003eHere is a general outline of how I accomplished it.\u003c/p\u003e\n\u003col\u003e\n\u003cli\u003eSet the display in graphics or text mode (both can use read cursors)\u003c/li\u003e\n\u003cli\u003eWrite the register to set RCURH0 (Read Cursor Horizontal Layer 0) (x position)\u003c/li\u003e\n\u003cli\u003eWrite the register to set RCURV0 (Read Cursor Vertical Layer 0) (y position)\u003c/li\u003e\n\u003cli\u003eActivate the MRWC (Memory Read/Write Control)\u003c/li\u003e\n\u003cli\u003eRead and discard the first returned 16-bit dummy value (noted in the datasheet)\u003c/li\u003e\n\u003cli\u003eRead the 1st byte for the color at the x,y cursor position\u003c/li\u003e\n\u003cli\u003eRead the 2nd byte for the color at the x,y cursor position\u003c/li\u003e\n\u003cli\u003eSwap the 1st byte and 2nd byte to produce the correct 16-bit order.\u003c/li\u003e\n\u003cli\u003eExtract the 16-bit value into 3 separate R,G,B values\u003c/li\u003e\n\u003c/ol\u003e\n\u003cp\u003eHere is the function I wrote for the adafruit_ra8875 library to add the capability to read a single pixel value.\u0026nbsp; Please keep in mind it took me almost 2 weeks to learn how to work with the display driver in order to make this functionality a reality. There is a learning curve coming from displayio displays to this completely different display.\u003c/p\u003e\n      \n\n\n\n\n\n\n\n\n\n\n\n\n\n\n","metadata":{}},{"element_type":"code","content":"def read_single_pixel(self, x: int, y: int, debug: bool = False) -\u003e int:\n        \"\"\"\n        Read the color of a single pixel from layer 0 of the display at x,y coordinates.\n\n        :param int x: X coordinate of the pixel.\n        :param int y: Y coordinate of the pixel.\n        :param bool debug: Flag to enable debug printing.\n        :return: Color of the pixel in RGB format (24-bit).\n        :rtype: int\n        \"\"\"\n        # Ensure x and y are within display bounds\n        if not (0 \u003c= x \u003c self.width and 0 \u003c= y \u003c self.height):\n            raise ValueError(f\"Coordinates ({x}, {y}) out of bounds for display size {self.width}x{self.height}\")\n        if debug:\n            print(f\"Reading pixel at ({x}, {y})\")\n\n        # Set graphics mode\n        self._gfx_mode()\n        # Set read cursor position for layer0 (800x480 8-bit bus mode 16-bit color mode)\n        self._write_reg16(reg.RCURH0, x, debug=False)\n        self._write_reg16(reg.RCURV0, y + self.vert_offset, debug=False)\n        # Set Memory Read/Write Control \n        self._read_reg(reg.MRWC)\n        # Read the color data\n        dummy = self._read_data16(debug=False)  # Dummy read\n        msb = self._read_data16(debug=False)    # MSB read\n\n        # Extract color components from MSB\n        green = (msb \u003e\u003e 8) \u0026 0xFF   # Extracting 8 bits of green component\n        blue = (msb \u003e\u003e 3) \u0026 0xFF   # Extracting 8 bits of blue component\n        red = (msb \u003c\u003c 3) \u0026 0xFF  # Extracting 8 bits of red component\n        if debug: \n            print(f\"Dummy: {dummy} MSB: {msb}\")\n            print(f\"Extracted Colors: {red}, {green}, {blue}\")\n            print(f\"Binary values: Red: {red:08b}, Green: {green:08b}, Blue: {blue:08b}\")\n        \n        return red, green, blue","metadata":{"language":"auto","linenums":false}},{"element_type":"text","content":"\n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n        \u003cp\u003eIt's a good idea to draw a cursor on the exact coordinates to be read.\u0026nbsp; So I also created a function to draw a cross-hair cursor.\u0026nbsp; One already exists in the built-in functions but it's not a 2-color cursor. This ensures that the x,y coordinates you want to sample are being shared to the cursor for sampling.\u0026nbsp; It should be obviously important to do the pixel read prior to drawing the cursor. ;)\u003c/p\u003e\n\u003cp\u003eIt's a 2 color cursor in case the color you're sampling is the same color as the cross-hair. You'll still be able to see some form of a cross-hair regardless of the background color.\u0026nbsp; I'm using a red crosshair with black outline but the colors in the function are configurable.\u0026nbsp;\u003c/p\u003e\n      \n\n\n\n\n\n\n\n\n\n\n\n\n","metadata":{}},{"element_type":"code","content":"def draw_cursor(self, x, y, inside_color, outline_color, debug: bool = False):\n        # Draw the outline of the cursor\n        for i in range(-4, 5):\n            self.pixel(x + i, y - 4, outline_color)  # Top line\n            self.pixel(x + i, y + 4, outline_color)  # Bottom line\n            self.pixel(x - 4, y + i, outline_color)  # Left line\n            self.pixel(x + 4, y + i, outline_color)  # Right line\n\n        # Draw the inside of the cursor\n        for i in range(-3, 4):\n            self.pixel(x + i, y, inside_color)  # Horizontal line\n            self.pixel(x, y + i, inside_color)  # Vertical line\n        if debug:\n            print(f\"Drawing Cursor at: {x},{y}\")","metadata":{"language":"auto","linenums":false}},{"element_type":"text","content":"\n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n        \u003cp\u003eAs you can see in the red squares the cross-hair would get lost on a red background.\u0026nbsp; A 2-color cross-hair makes moving and finding the x,y coordinate you want to sample much easier.\u003c/p\u003e\n\u003cp\u003eKeep in mind these rectangles are from the color test BMP image and not vector rectangles drawn by the display.\u0026nbsp; So if we can accurately read these colors then we'll know we're actually reading a pixel from the display itself.\u0026nbsp;\u003c/p\u003e\n\u003cp\u003eInstead of reading 1 individual pixel I want to read 12 individual pixels to ensure the function can be repeatedly used as intended.\u003c/p\u003e\n      \n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n","metadata":{}},{"element_type":"user_image","content":"https://cdn-learn.adafruit.com/user_assets/assets/000/000/875/original/IMG_1611.jpg?1715007397","metadata":{"caption":"2-Color crosshair for pixel reading"}},{"element_type":"text","content":"\n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n        \u003cp\u003eSo how to confirm the pixel values we're reading are actually correct?\u0026nbsp; We can use the adafruit_ra8875 library existing fill_rect function to replicate the 53x53 rectangles to another portion of the display.\u003c/p\u003e\n      \n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n","metadata":{}},{"element_type":"code","content":"code.py example\n# This will draw a 53x53 red rectangle beginning at coordinates 0,0\ndisplay.fill_rect(0, 0, 53, 53, color565(255,0,0)","metadata":{"language":"auto","linenums":false}},{"element_type":"text","content":"\n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n        \u003cp\u003eBut we don't want to draw just 1 rectangle, we need all 12 to confirm the replication.\u0026nbsp; So we just repeat this 12 times at the appropriate coordinates. You could use a for loop to do this but I've discovered a for loop is actually much slower than just repeating it manually 12 times.\u0026nbsp; It is about 12 times slower actually.\u0026nbsp; By manually repeating the fill_rect command 12 times in code.py the draw time is almost mind-blowing instantaneous!\u0026nbsp; Something to keep in mind when using for loops with this display.\u0026nbsp; It definitely prefers sequential commands vs for loops.\u003c/p\u003e\n\u003cp\u003eHere is an early attempt at a failed color conversion.\u0026nbsp; Ultimately the issue ended up being the way the BMP itself was encoded.\u0026nbsp; That's another thing to note is that not all BMP's are the same. Windows will convert a BMP differently than GIMP or an online BMP converter.\u003c/p\u003e\n      \n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n","metadata":{}},{"element_type":"user_image","content":"https://cdn-learn.adafruit.com/user_assets/assets/000/000/876/original/Attempted_Color_Conversion.jpg?1715009294","metadata":{"caption":"An early failed color conversion attempt."}},{"element_type":"text","content":"\n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n        \u003cp\u003eEventually I had to add in a lot of debugging prints to the function and other functions to figure out why the color wasn't reading correctly or if it could actually read the display at all.\u0026nbsp; I went through days of this and almost gave up many times.\u0026nbsp; Eventually I started printing the binary values directly and that's when I noticed the discrepancy between the binary and color565 values.\u003c/p\u003e\n\u003cp\u003eEventually I did get it to work and it works quite well.\u0026nbsp; The color reproduction isn't 100% accurate due to 24-bit to 16-bit conversion but that's a refinement for another day.\u003c/p\u003e\n      \n\n\n\n\n\n\n\n\n\n","metadata":{}},{"element_type":"user_image","content":"https://cdn-learn.adafruit.com/user_assets/assets/000/000/877/original/Correct_Display_Read.jpg?1715009586","metadata":{"caption":"Reading 12 individual pixels from the display and replicating them with fill_rect."}},{"element_type":"text","content":"\n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n        \u003cp\u003eHere is the sum of my efforts.\u0026nbsp; A couple of new functions for the library and a code.py example to read pixels from the RA8875 display with fill_rect to confirm their values.\u003c/p\u003e\n      \n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n","metadata":{}},{"element_type":"code","content":"# SPDX-FileCopyrightText: 2024 DJDevon3\n# SPDX-License-Identifier: MIT\n\"\"\"RA8875 Read Single Pixel example\"\"\"\n\nimport time\nimport busio\nimport digitalio\nimport board\n\nfrom adafruit_ra8875 import ra8875\nfrom adafruit_ra8875.bmp import BMP\nfrom adafruit_ra8875.ra8875 import color565\n\n# Configuration for CS and RST pins:\ncs_pin = digitalio.DigitalInOut(board.D9)\nrst_pin = digitalio.DigitalInOut(board.D10)\n\n# Config for display baudrate (default max is 6mhz):\nBAUDRATE = 6000000\n\n# Setup SPI bus using hardware SPI:\nspi = busio.SPI(clock=board.SCK, MOSI=board.MOSI, MISO=board.MISO)\n\n# Create and setup the RA8875 display:\ndisplay = ra8875.RA8875(spi, cs=cs_pin, rst=rst_pin, baudrate=BAUDRATE)\ndisplay.init()\n\nBLACK = color565(0, 0, 0)\nRED = color565(255, 0, 0)\nGREEN = color565(0, 255, 0)\nBLUE = color565(0, 0, 255)\nYELLOW = color565(255, 255, 0)\nCYAN = color565(0, 255, 255)\nMAGENTA = color565(255, 0, 255)\nWHITE = color565(255, 255, 255)\n\n# Load the bitmap image\nbitmap = BMP(\"/Color_test_chart2.bmp\")\n\n# Center BMP image on the display\nx_position = (display.width // 2) - (bitmap.width // 2)\ny_position = (display.height // 2) - (bitmap.height // 2)\n\n# Fill entire display background with white\ndisplay.fill(WHITE)\nprint(f\"Filled display layer0 with white\\n\")\n\n# Draw BMP (bottom to top)\nbitmap.draw_bmp(display, x_position, y_position)\n\n# Coordinates inside of a 53x53 red square in the bmp\nX1 = 320\nY1 = 190\n# Coordinates inside of a 53x53 blue square in the bmp\nX2 = 370\nY2 = 190\n# Coordinates inside of a 53x53 purple square in the bmp\nX3 = 425\nY3 = 190\n# Coordinates inside of a 53x53 yellow square in the bmp\nX4 = 485\nY4 = 190\n# Coordinates inside of a 53x53 green square in the bmp\nX5 = 320\nY5 = 240\n# Coordinates inside of a 53x53 cyan square in the bmp\nX6 = 370\nY6 = 240\n# Coordinates inside of a 53x53 white square in the bmp\nX7 = 425\nY7 = 240\n# Coordinates inside of a 53x53 black square in the bmp\nX8 = 485\nY8 = 240\n# Coordinates inside of a 53x53 yellow square in the bmp\nX9 = 320\nY9 = 290\n# Coordinates inside of a 53x53 red square in the bmp\nX10 = 370\nY10 = 290\n# Coordinates inside of a 53x53 green square in the bmp\nX11 = 425\nY11 = 290\n# Coordinates inside of a 53x53 blue square in the bmp\nX12 = 485\nY12 = 290\n\n# List of color sampling coordinates\ncoordinates = [\n    (X1, Y1), (X2, Y2), (X3, Y3), (X4, Y4),\n    (X5, Y5), (X6, Y6), (X7, Y7), (X8, Y8),\n    (X9, Y9), (X10, Y10), (X11, Y11), (X12, Y12)\n]\n\n# Giving them names makes it easier to spot errors\ncolor_names = [\n    \"Red\", \"Blue\", \"Purple\", \"Yellow\",\n    \"Green\", \"Cyan\", \"White\", \"Black\",\n    \"Yellow\", \"Red\", \"Green\", \"Blue\"\n]\n\n# Starting x,y for color rectangles to create\nrect_coordinates = [\n    (0, 0), (53, 0), (106, 0), (159, 0),\n    (0, 53), (53, 53), (106, 53), (159, 53),\n    (0, 106), (53, 106), (106, 106), (159, 106)\n]\n\n# Read every pixel at listed coordinates\n# Returns colors as r,g,b\n# Creates filled rectangles using r,g,b to confirm color sample \nfor i, (x, y) in enumerate(coordinates):\n    color = display.read_single_pixel(x, y)\n    print(f\"color{i+1} at ({x},{y}): {color_names[i]} - {color}\")\n    time.sleep(0.1)\n    rect_x, rect_y = rect_coordinates[i]\n    display.fill_rect(rect_x, rect_y, 53, 53, color565(color))\n\n# Draws cross-hair to confirm sampled coordinates\n# This can only happen after the sample is taken\ndisplay.draw_cursor(X1,Y1,RED,BLACK)\ndisplay.draw_cursor(X2,Y2,RED,BLACK)\ndisplay.draw_cursor(X3,Y3,RED,BLACK)\ndisplay.draw_cursor(X4,Y4,RED,BLACK)\ndisplay.draw_cursor(X5,Y5,RED,BLACK)\ndisplay.draw_cursor(X6,Y6,RED,BLACK)\ndisplay.draw_cursor(X7,Y7,RED,BLACK)\ndisplay.draw_cursor(X8,Y8,RED,BLACK)\ndisplay.draw_cursor(X9,Y9,RED,BLACK)\ndisplay.draw_cursor(X10,Y10,RED,BLACK)\ndisplay.draw_cursor(X11,Y11,RED,BLACK)\ndisplay.draw_cursor(X12,Y12,RED,BLACK)","metadata":{"language":"python","linenums":false}},{"element_type":"text","content":"\n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n        \u003cp\u003eNow that the RA8875 library can read single pixels it will open the door for others in the future to build more complex features and perhaps someday... a hardware accelerated version of displayio.\u003c/p\u003e\n      \n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n","metadata":{}}]