Skip to content

Badger2350: Update LUTs for nice grays - #11671

Merged
dhalbert merged 3 commits into
adafruit:mainfrom
bablokb:badger2350_luts
Oct 27, 2026
Merged

dhalbert merged 3 commits into
adafruit:mainfrom
bablokb:badger2350_luts

Conversation

@bablokb

@bablokb bablokb commented Oct 26, 2026

Copy link
Copy Markdown

Update the LUTs for nice grays. Basically a copy of the LUTs developed by @mikeysklar for the MagTag (latest version) with minor tweaks.

@mikeysklar: the only relevant difference is that I added the EOPT command, which made a huge difference for me. With EOPT, there are no more specles at all, I have an absolute clean white. Since I only have an old MagTag, I cannot test if this would also improve the MagTag rendering, but maybe you can give it a try.

@mikeysklar

Copy link
Copy Markdown
Collaborator

I'm testing this on some MagTag's this morning. Will report back soon.

@mikeysklar

mikeysklar commented Oct 27, 2026 •

Copy link
Copy Markdown
Collaborator

I tried 0x3F = 0x22 before the LUT load on two MagTags (CP 11.0.0-alpha.2). The darks look a little darker with EOPT, but speckle wasn't an issue on both boards.

Top: FPC-7519 with EOPT 0x22. Bottom: FPC-A005 without it.

eopt-cmp

Did the speckles go away from EOPT alone, or together with the VCOM 0x20 and RP changes?

Test code (needs the eopt option below added to adafruit_ssd1680)
# SPDX-FileCopyrightText: 2026 Mikey Sklar, written for Adafruit Industries
# SPDX-FileCopyrightText: 2025 Scott Shawcroft, written for Adafruit Industries
# SPDX-FileCopyrightText: Copyright (c) 2021 Melissa LeBlanc-Williams for Adafruit Industries
#
# SPDX-License-Identifier: Unlicense

"""EOPT A/B test for the MagTag 2.9" 296x128 display in 2bit grayscale mode.

Detects the FPC-A005 or FPC-7519rev.b panel from the SSD1680 User ID register, then
draws a sectioned 4-gray test pattern. Set EOPT = "0x22" in settings.toml to send the
SSD1680 End Option (0x3F) command before the LUT; leave it unset to skip it.
"""

import os
import time

import bitmaptools
import board
import busio
import displayio
import terminalio
from adafruit_display_text import label
from fourwire import FourWire

import adafruit_ssd1680

WHITE, LIGHT, DARK, BLACK = range(4)

displayio.release_displays()

panel_id = adafruit_ssd1680.detect_ssd1680_panel(
    board.EPD_MOSI, board.EPD_SCK, board.EPD_CS, board.EPD_DC, board.EPD_RESET
)
print(f"SSD1680 User ID first byte: 0x{panel_id:02x}")

if panel_id == 0x00:
    panel_name = "FPC-A005"
    vcom = 0x28
    colstart = 0
    lut = adafruit_ssd1680.FPC_A005_LUT
else:
    panel_name = f"FPC-7519 0x{panel_id:02x}"
    vcom = 0x24
    colstart = 8
    lut = adafruit_ssd1680.FPC7519_LUT

eopt_setting = os.getenv("EOPT")
eopt = int(eopt_setting, 16) if eopt_setting else None
print(f"Panel: {panel_name}, EOPT: {eopt_setting}")

spi = busio.SPI(board.EPD_SCK, board.EPD_MOSI)
display_bus = FourWire(
    spi, command=board.EPD_DC, chip_select=board.EPD_CS, reset=board.EPD_RESET, baudrate=1000000
)
time.sleep(1)

display = adafruit_ssd1680.SSD1680(
    display_bus,
    width=296,
    height=128,
    busy_pin=board.EPD_BUSY,
    rotation=270,
    colstart=colstart,
    vcom=vcom,
    vsh2=0xAE,
    custom_lut=lut,
    eopt=eopt,
    grayscale=True,
)

W, H = 296, 128
HEADER = 26
bmp = displayio.Bitmap(W, H, 4)
palette = displayio.Palette(4)
palette[WHITE] = 0xFFFFFF
palette[LIGHT] = 0xAAAAAA
palette[DARK] = 0x555555
palette[BLACK] = 0x000000


def rect(x0, y0, x1, y1, color):
    """Outline from (x0, y0) to (x1, y1) inclusive."""
    bitmaptools.draw_line(bmp, x0, y0, x1, y0, color)
    bitmaptools.draw_line(bmp, x0, y1, x1, y1, color)
    bitmaptools.draw_line(bmp, x0, y0, x0, y1, color)
    bitmaptools.draw_line(bmp, x1, y0, x1, y1, color)


# Header bar: black, so the white label text stands out
bitmaptools.fill_region(bmp, 0, 0, W, HEADER, BLACK)

# Body is split into four 74px sections
top = HEADER + 2
mid = top + (H - top) // 2

# Section 1: the four gray levels as flat swatches over a large white field
for i, color in enumerate((WHITE, LIGHT, DARK, BLACK)):
    bitmaptools.fill_region(bmp, 1 + i * 18, top, 1 + i * 18 + 18, mid, color)
rect(1, top, 72, mid - 1, BLACK)

# Section 2: 1px black grid on white, then diagonals in each gray
for x in range(76, 146, 4):
    bitmaptools.draw_line(bmp, x, top, x, mid - 1, BLACK)
for y in range(top, mid, 4):
    bitmaptools.draw_line(bmp, 76, y, 145, y, BLACK)
for y in range(mid, H):
    for x in range(76, 146):
        if (x + y) % 6 == 0:
            bmp[x, y] = (LIGHT, DARK, BLACK)[((x + y) // 6) % 3]

# Section 3: nested boxes stepping through all four levels, gray-on-gray edges
for i in range(0, 37, 3):
    color = (BLACK, DARK, LIGHT, WHITE)[(i // 3) % 4]
    bitmaptools.fill_region(bmp, 149 + i, top + i // 2, 222 - i, H - i // 2, color)

# Section 4: checkerboards for each gray pair, then single-pixel dots on white
pairs = ((WHITE, BLACK), (LIGHT, DARK), (WHITE, LIGHT), (DARK, BLACK))
for q, (a, b) in enumerate(pairs):
    qx = 224 + (q % 2) * 36
    qy = top + (q // 2) * ((mid - top) // 2)
    for y in range(qy, qy + (mid - top) // 2):
        for x in range(qx, qx + 36):
            bmp[x, y] = a if ((x // 2) + (y // 2)) % 2 else b
for y in range(mid + 2, H - 1, 3):
    for x in range(225 + (y % 2), W - 1, 3):
        bmp[x, y] = BLACK if (x + y) % 2 else DARK

# Section dividers
for x in (73, 147, 222):
    bitmaptools.draw_line(bmp, x, HEADER, x, H - 1, BLACK)
bitmaptools.draw_line(bmp, 0, HEADER, W - 1, HEADER, BLACK)

g = displayio.Group()
g.append(displayio.TileGrid(bmp, pixel_shader=palette))
title = f"EOPT {eopt_setting}" if eopt is not None else "NO EOPT"
g.append(label.Label(terminalio.FONT, text=title, color=0xFFFFFF, scale=2, x=4, y=12))
g.append(
    label.Label(
        terminalio.FONT, text=panel_name, color=0xFFFFFF, anchor_point=(1.0, 0.5),
        anchored_position=(W - 4, 13),
    )
)

display.root_group = g
display.refresh()

print("refreshed")

time.sleep(display.time_to_refresh + 5)
print("waited correct time")

while True:
    time.sleep(10)
adafruit_ssd1680 change for the eopt option
@@ -177,10 +177,18 @@
           Set vsh2 voltage register value
         * *custom_lut* (``bytes``) --
           Custom look-up table settings
+        * *eopt* (``int``) --
+          End Option (0x3F) register value, sent before the LUT. None skips the command
     """
 
     def __init__(
-        self, bus: FourWire, vcom: int = 0x36, vsh2: int = 0x00, custom_lut: bytes = b"", **kwargs
+        self,
+        bus: FourWire,
+        vcom: int = 0x36,
+        vsh2: int = 0x00,
+        custom_lut: bytes = b"",
+        eopt: int = None,
+        **kwargs,
     ) -> None:
         if "colstart" not in kwargs:
             kwargs["colstart"] = 8
@@ -196,7 +204,8 @@
             load_lut = b"\x32" + len(custom_lut).to_bytes(2) + custom_lut
             display_update_mode[-1] = 0xC7
 
-        start_sequence = bytearray(_START_SEQUENCE + load_lut + display_update_mode)
+        eopt_cmd = b"\x3f\x00\x01" + bytes([eopt]) if eopt is not None else b""
+        start_sequence = bytearray(_START_SEQUENCE + eopt_cmd + load_lut + display_update_mode)
         start_sequence[15] = vcom
 
         start_sequence[24] = vsh2

mikeysklar
mikeysklar previously approved these changes Oct 27, 2026

@mikeysklar mikeysklar left a comment •

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One question: the BusDevice and Register frozen libs in mpconfigboard.mk aren't mentioned in the description.

Are they necessary?

Comment thread ports/raspberrypi/boards/pimoroni_badger2350/mpconfigboard.mk Outdated

@mikeysklar mikeysklar left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@dhalbert dhalbert left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks!

@Gadgetoid of interest to you

@dhalbert
dhalbert merged commit 8e22769 into adafruit:main Oct 27, 2026
16 checks passed
@bablokb
bablokb deleted the badger2350_luts branch October 27, 2026 13:31
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants