Skip to content

Support newer 2.9" grayscale #36

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Jul 30, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 34 additions & 15 deletions adafruit_ssd1680.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,19 +42,20 @@
__repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_SSD1680.git"

_START_SEQUENCE = (
b"\x12\x80\x14" # soft reset and wait 20ms
b"\x11\x01\x03" # Ram data entry mode
b"\x3c\x01\x05" # border color
b"\x2c\x01\x36" # Set vcom voltage
b"\x03\x01\x17" # Set gate voltage
b"\x04\x03\x41\x00\x32" # Set source voltage
b"\x4e\x01\x01" # ram x count
b"\x4f\x02\x00\x00" # ram y count
b"\x01\x03\x00\x00\x00" # set display size
b"\x22\x01\xf4" # display update mode
b"\x12\x80\x00\x14" # soft reset and wait 20ms
b"\x11\x00\x01\x03" # Ram data entry mode
b"\x3c\x00\x01\x03" # border color
b"\x2c\x00\x01\x36" # Set vcom voltage
b"\x03\x00\x01\x17" # Set gate voltage
b"\x04\x00\x03\x41\xae\x32" # Set source voltage
b"\x4e\x00\x01\x01" # ram x count
b"\x4f\x00\x02\x00\x00" # ram y count
b"\x01\x00\x03\x00\x00\x00" # set display size
)

_STOP_SEQUENCE = b"\x10\x81\x01\x64" # Deep Sleep
_DISPLAY_UPDATE_MODE = b"\x22\x00\x01\xf4" # display update mode

_STOP_SEQUENCE = b"\x10\x80\x01\x01\x64" # Deep Sleep


# pylint: disable=too-few-public-methods
Expand All @@ -72,9 +73,17 @@ class SSD1680(EPaperDisplay):
Display height
* *rotation* (``int``) --
Display rotation
* *vcom* (``int``) --
Set vcom voltage register value
* *vsh2* (``int``) --
Set vsh2 voltage register value
* *custom_lut* (``bytes``) --
Custom look-up table settings
"""

def __init__(self, bus: FourWire, **kwargs) -> None:
def __init__(
self, bus: FourWire, vcom: int = 0x36, vsh2: int = 0x00, custom_lut: bytes = b"", **kwargs
) -> None:
if "colstart" not in kwargs:
kwargs["colstart"] = 8
stop_sequence = bytearray(_STOP_SEQUENCE)
Expand All @@ -83,14 +92,23 @@ def __init__(self, bus: FourWire, **kwargs) -> None:
except RuntimeError:
# No reset pin defined, so no deep sleeping
stop_sequence = b""
load_lut = b""
display_update_mode = bytearray(_DISPLAY_UPDATE_MODE)
if custom_lut:
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)
start_sequence[15] = vcom

start_sequence[24] = vsh2

start_sequence = bytearray(_START_SEQUENCE)
width = kwargs["width"]
height = kwargs["height"]
if "rotation" in kwargs and kwargs["rotation"] % 180 != 90:
width, height = height, width
start_sequence[29] = (width - 1) & 0xFF
start_sequence[30] = ((width - 1) >> 8) & 0xFF
start_sequence[38] = (width - 1) & 0xFF
start_sequence[39] = ((width - 1) >> 8) & 0xFF

super().__init__(
bus,
Expand All @@ -109,4 +127,5 @@ def __init__(self, bus: FourWire, **kwargs) -> None:
refresh_display_command=0x20,
always_toggle_chip_select=False,
address_little_endian=True,
two_byte_sequence_length=True,
)
Binary file added examples/display-ruler-640x360.bmp
Binary file not shown.
Binary file removed examples/display-ruler.bmp
Binary file not shown.
2 changes: 1 addition & 1 deletion examples/ssd1680_2.13_featherwing.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@
g = displayio.Group()


pic = displayio.OnDiskBitmap("/display-ruler.bmp")
pic = displayio.OnDiskBitmap("/display-ruler-640x360.bmp")

t = displayio.TileGrid(pic, pixel_shader=pic.pixel_shader)

Expand Down
2 changes: 1 addition & 1 deletion examples/ssd1680_2.13_mono_eink_bonnet.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@
g = displayio.Group()


pic = displayio.OnDiskBitmap("/display-ruler.bmp")
pic = displayio.OnDiskBitmap("/display-ruler-640x360.bmp")
t = displayio.TileGrid(pic, pixel_shader=pic.pixel_shader)
g.append(t)

Expand Down
2 changes: 1 addition & 1 deletion examples/ssd1680_2.13_tricolor_breakout.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@
g = displayio.Group()


pic = displayio.OnDiskBitmap("/display-ruler.bmp")
pic = displayio.OnDiskBitmap("/display-ruler-640x360.bmp")

t = displayio.TileGrid(pic, pixel_shader=pic.pixel_shader)

Expand Down
89 changes: 89 additions & 0 deletions examples/ssd1680_2.9_grayscale_magtag2025.py
Copy link
Contributor

Choose a reason for hiding this comment

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

could the grayscale lut array live in the adafruit_ssd1680.py file? i could see it being confusing to folks having it in code.py

Copy link
Member Author

Choose a reason for hiding this comment

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

We should consider having per-panel modules to hide the LUT code. I wouldn't want it in the driver file itself because it is panel dependent.

Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
# SPDX-FileCopyrightText: 2025 Scott Shawcroft, written for Adafruit Industries
# SPDX-FileCopyrightText: Copyright (c) 2021 Melissa LeBlanc-Williams for Adafruit Industries
#
# SPDX-License-Identifier: Unlicense

"""Simple test script for 2.9" 296x128 display. This example runs it in 2bit grayscale mode."""

import time

import board
import busio
import displayio
from fourwire import FourWire

import adafruit_ssd1680

displayio.release_displays()

# This pinout works on a MagTag with the newer screen and may need to be altered for other boards.
spi = busio.SPI(board.EPD_SCK, board.EPD_MOSI) # Uses SCK and MOSI
epd_cs = board.EPD_CS
epd_dc = board.EPD_DC
epd_reset = board.EPD_RESET
epd_busy = board.EPD_BUSY

display_bus = FourWire(spi, command=epd_dc, chip_select=epd_cs, reset=epd_reset, baudrate=1000000)
time.sleep(1)

ti_290mfgn_gray4_lut_code = (
b"\x2a\x60\x15\x00\x00\x00\x00\x00\x00\x00\x00\x00" # VS L0
b"\x20\x60\x10\x00\x00\x00\x00\x00\x00\x00\x00\x00" # VS L1
b"\x28\x60\x14\x00\x00\x00\x00\x00\x00\x00\x00\x00" # VS L2
b"\x00\x60\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" # VS L3
b"\x00\x90\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" # VS L4
b"\x00\x02\x00\x05\x14\x00\x00" # TP, SR, RP of Group0
b"\x1e\x1e\x00\x00\x00\x00\x01" # TP, SR, RP of Group1
b"\x00\x02\x00\x05\x14\x00\x00" # TP, SR, RP of Group2
b"\x00\x00\x00\x00\x00\x00\x00" # TP, SR, RP of Group3
b"\x00\x00\x00\x00\x00\x00\x00" # TP, SR, RP of Group4
b"\x00\x00\x00\x00\x00\x00\x00" # TP, SR, RP of Group5
b"\x00\x00\x00\x00\x00\x00\x00" # TP, SR, RP of Group6
b"\x00\x00\x00\x00\x00\x00\x00" # TP, SR, RP of Group7
b"\x00\x00\x00\x00\x00\x00\x00" # TP, SR, RP of Group8
b"\x00\x00\x00\x00\x00\x00\x00" # TP, SR, RP of Group9
b"\x00\x00\x00\x00\x00\x00\x00" # TP, SR, RP of Group10
b"\x00\x00\x00\x00\x00\x00\x00" # TP, SR, RP of Group11
b"\x24\x22\x22\x22\x23\x32\x00\x00\x00" # FR, XON
)

if len(ti_290mfgn_gray4_lut_code) != 153:
raise ValueError("ti_290mfgn_gray4_lut_code is not the correct length")

# For issues with display not updating top/bottom rows correctly set colstart to 8, 0, or -8
display = adafruit_ssd1680.SSD1680(
display_bus,
width=296,
height=128,
busy_pin=epd_busy,
rotation=270,
colstart=0,
vcom=0x28,
vsh2=0xAE,
custom_lut=ti_290mfgn_gray4_lut_code,
grayscale=True,
)

g = displayio.Group()

pic = displayio.OnDiskBitmap("/display-ruler-640x360.bmp")
t = displayio.TileGrid(pic, pixel_shader=pic.pixel_shader)
g.append(t)

display.root_group = g

display.refresh()

print("refreshed")

time.sleep(display.time_to_refresh + 5)
# Always refresh a little longer. It's not a problem to refresh
# a few seconds more, but it's terrible to refresh too early
# (the display will throw an exception when if the refresh
# is too soon)
print("waited correct time")


# Keep the display the same
while True:
time.sleep(10)
56 changes: 56 additions & 0 deletions examples/ssd1680_2.9_mono_magtag2025.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
# SPDX-FileCopyrightText: 2025 Scott Shawcroft, written for Adafruit Industries
# SPDX-FileCopyrightText: Copyright (c) 2021 Melissa LeBlanc-Williams for Adafruit Industries
#
# SPDX-License-Identifier: Unlicense

"""Simple test script for 2.9" 296x128 display. This example runs it in mono mode."""

import time

import board
import busio
import displayio
from fourwire import FourWire

import adafruit_ssd1680

displayio.release_displays()

# This pinout works on a MagTag with the newer screen and may need to be altered for other boards.
spi = busio.SPI(board.EPD_SCK, board.EPD_MOSI) # Uses SCK and MOSI
epd_cs = board.EPD_CS
epd_dc = board.EPD_DC
epd_reset = board.EPD_RESET
epd_busy = board.EPD_BUSY

display_bus = FourWire(spi, command=epd_dc, chip_select=epd_cs, reset=epd_reset, baudrate=1000000)
time.sleep(1)

# For issues with display not updating top/bottom rows correctly set colstart to 8, 0, or -8
display = adafruit_ssd1680.SSD1680(
display_bus, width=296, height=128, busy_pin=epd_busy, rotation=270, colstart=0
)

g = displayio.Group()

pic = displayio.OnDiskBitmap("/display-ruler-640x360.bmp")
t = displayio.TileGrid(pic, pixel_shader=pic.pixel_shader)
g.append(t)

display.root_group = g

display.refresh()

print("refreshed")

time.sleep(display.time_to_refresh + 5)
# Always refresh a little longer. It's not a problem to refresh
# a few seconds more, but it's terrible to refresh too early
# (the display will throw an exception when if the refresh
# is too soon)
print("waited correct time")


# Keep the display the same
while True:
time.sleep(10)
2 changes: 1 addition & 1 deletion examples/ssd1680_2.9_tricolor_breakout.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@
g = displayio.Group()


pic = displayio.OnDiskBitmap("/display-ruler.bmp")
pic = displayio.OnDiskBitmap("/display-ruler-640x360.bmp")

t = displayio.TileGrid(pic, pixel_shader=pic.pixel_shader)

Expand Down
2 changes: 1 addition & 1 deletion examples/ssd1680_simpletest.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@

# Note: Check the name of the file. Sometimes the dash is changed to an underscore

pic = displayio.OnDiskBitmap("/display-ruler.bmp")
pic = displayio.OnDiskBitmap("/display-ruler-640x360.bmp")
t = displayio.TileGrid(pic, pixel_shader=pic.pixel_shader)
g.append(t)

Expand Down