|
108 | 108 | ) |
109 | 109 |
|
110 | 110 |
|
| 111 | +_SSD1683_START_SEQUENCE = ( |
| 112 | + b"\x12\x80\x00\xc8" # SW_RESET + 200ms delay (cold-boot charge-pump settle) |
| 113 | + b"\x0c\x00\x04\x8b\x9c\xa4\x0f" # BOOST_SOFTSTART |
| 114 | + b"\x11\x00\x01\x03" # RAM data entry mode |
| 115 | + b"\x3c\x00\x01\x03" # border |
| 116 | + b"\x21\x00\x02\x00\x00" # DISP_CTRL1 (no source-line shift) |
| 117 | + b"\x3f\x00\x01\x07" # END_OPTION |
| 118 | + b"\x03\x00\x01\x17" # gate voltage |
| 119 | + b"\x04\x00\x03\x41\xa8\x32" # source voltage |
| 120 | + b"\x2c\x00\x01\x30" # VCOM (default 0x30, offset 41) |
| 121 | + b"\x4e\x00\x01\x00" # RAM X count |
| 122 | + b"\x4f\x00\x02\x00\x00" # RAM Y count |
| 123 | + b"\x01\x00\x03\x00\x00\x00" # driver output control (gate lo/hi at offsets 54/55) |
| 124 | +) |
| 125 | + |
| 126 | +_SSD1683_DISPLAY_UPDATE_MODE = b"\x22\x00\x01\xcf" # 4-gray LUT-based update |
| 127 | + |
| 128 | +# 4-gray waveform LUT for GDEY042T81 / FPC-190 — native GxEPD2 order (unswapped). |
| 129 | +# Correct level order relies on the BW/Color RAM banks being swapped in __init__ |
| 130 | +# (write_black_ram_command=0x26, write_color_ram_command=0x24). |
| 131 | +SSD1683_GRAY4_LUT = bytes.fromhex( |
| 132 | + # 5 waveform groups x 6 phase-rows (7 B each); GxEPD2 GDEY042T81 lut_4G[0:210] |
| 133 | + "010A1B0F030101" # group 0 |
| 134 | + "050A010A010101" |
| 135 | + "05080302040101" |
| 136 | + "01040402000101" |
| 137 | + "01000000000101" |
| 138 | + "01000000000101" |
| 139 | + "010A1B0F030101" # group 1 |
| 140 | + "054A018A010101" |
| 141 | + "05480382840101" |
| 142 | + "01848482000101" |
| 143 | + "01000000000101" |
| 144 | + "01000000000101" |
| 145 | + "010A1B8F030101" # group 2 |
| 146 | + "054A018A010101" |
| 147 | + "05488382040101" |
| 148 | + "01040402000101" |
| 149 | + "01000000000101" |
| 150 | + "01000000000101" |
| 151 | + "018A1B8F030101" # group 3 |
| 152 | + "054A018A010101" |
| 153 | + "05488302040101" |
| 154 | + "01040402000101" |
| 155 | + "01000000000101" |
| 156 | + "01000000000101" |
| 157 | + "018A9B8F030101" # group 4 |
| 158 | + "054A018A010101" |
| 159 | + "05480342040101" |
| 160 | + "01040442000101" |
| 161 | + "01000000000101" |
| 162 | + "01000000000101" |
| 163 | + "00000000000000" # 2 reserved phase-rows |
| 164 | + "00000000000000" |
| 165 | + "020000" # frame-rate / repeat tail |
| 166 | +) # 227 bytes — GDEY042T81 / FPC-190 (matches Adafruit_EPD ssd1683.py) |
| 167 | + |
| 168 | + |
111 | 169 | def detect_ssd1680_panel(data_pin, clk_pin, cs_pin, dc_pin, rst_pin): |
112 | 170 | """Read SSD1680 User ID (register 0x2E) via half-duplex bitbang SPI. |
113 | 171 |
|
@@ -233,3 +291,80 @@ def __init__( |
233 | 291 | address_little_endian=True, |
234 | 292 | two_byte_sequence_length=True, |
235 | 293 | ) |
| 294 | + |
| 295 | + |
| 296 | +# pylint: disable=too-few-public-methods |
| 297 | +class SSD1683(EPaperDisplay): |
| 298 | + r"""SSD1683 driver for 4-gray grayscale e-paper displays. |
| 299 | +
|
| 300 | + Designed for the 4.2" 400×300 GDEY042T81 panel (#6381, FPC-190 ribbon). |
| 301 | + Gate lines run along the height dimension (300), so DRIVER_CONTROL is set |
| 302 | + from height rather than width. |
| 303 | +
|
| 304 | + :param bus: The data bus the display is on |
| 305 | + :param \**kwargs: |
| 306 | + See below |
| 307 | +
|
| 308 | + :Keyword Arguments: |
| 309 | + * *width* (``int``) -- |
| 310 | + Display width (400 for the 4.2" panel) |
| 311 | + * *height* (``int``) -- |
| 312 | + Display height (300 for the 4.2" panel) |
| 313 | + * *rotation* (``int``) -- |
| 314 | + Display rotation |
| 315 | + * *vcom* (``int``) -- |
| 316 | + VCOM voltage register value (default 0x30) |
| 317 | + * *custom_lut* (``bytes``) -- |
| 318 | + Custom waveform LUT; pass ``SSD1683_GRAY4_LUT`` for 4-gray mode |
| 319 | + """ |
| 320 | + |
| 321 | + def __init__(self, bus: FourWire, vcom: int = 0x30, custom_lut: bytes = b"", **kwargs) -> None: |
| 322 | + kwargs["grayscale"] = True # SSD1683 always runs in 4-gray mode |
| 323 | + if "colstart" not in kwargs: |
| 324 | + kwargs["colstart"] = 0 |
| 325 | + stop_sequence = bytearray(_STOP_SEQUENCE) |
| 326 | + try: |
| 327 | + bus.reset() |
| 328 | + # Cold-boot settle: after a true power-on, the controller's regulators |
| 329 | + # need time before the start sequence loads the LUT (mirrors the |
| 330 | + # framebuffer driver's sleep(0.1)+busy_wait). A short delay here plus the |
| 331 | + # longer SW_RESET delay below prevents a corrupt-LUT speckle on cold boot. |
| 332 | + time.sleep(0.2) |
| 333 | + except RuntimeError: |
| 334 | + stop_sequence = b"" |
| 335 | + load_lut = b"" |
| 336 | + display_update_mode = bytearray(_SSD1683_DISPLAY_UPDATE_MODE) |
| 337 | + if custom_lut: |
| 338 | + load_lut = b"\x32" + len(custom_lut).to_bytes(2) + custom_lut |
| 339 | + |
| 340 | + start_sequence = bytearray(_SSD1683_START_SEQUENCE + load_lut + display_update_mode) |
| 341 | + start_sequence[41] = vcom |
| 342 | + |
| 343 | + width = kwargs["width"] |
| 344 | + height = kwargs["height"] |
| 345 | + # Save gate count before any rotation swap — always the physical height (300) |
| 346 | + gate_height = height |
| 347 | + if "rotation" in kwargs and kwargs["rotation"] % 180 != 90: |
| 348 | + width, height = height, width |
| 349 | + start_sequence[54] = (gate_height - 1) & 0xFF |
| 350 | + start_sequence[55] = ((gate_height - 1) >> 8) & 0xFF |
| 351 | + |
| 352 | + super().__init__( |
| 353 | + bus, |
| 354 | + start_sequence, |
| 355 | + stop_sequence, |
| 356 | + **kwargs, |
| 357 | + ram_width=255, # <256 forces 1-byte X (0x44) addressing; SSD168x X is byte-addressed |
| 358 | + ram_height=300, |
| 359 | + busy_state=True, |
| 360 | + write_black_ram_command=0x26, # swapped vs SSD1680: displayio packs luma |
| 361 | + write_color_ram_command=0x24, # bit7->black,bit6->color; SSD1683 wants the opposite |
| 362 | + set_column_window_command=0x44, |
| 363 | + set_row_window_command=0x45, |
| 364 | + set_current_column_command=0x4E, |
| 365 | + set_current_row_command=0x4F, |
| 366 | + refresh_display_command=0x20, |
| 367 | + always_toggle_chip_select=False, |
| 368 | + address_little_endian=True, |
| 369 | + two_byte_sequence_length=True, |
| 370 | + ) |
0 commit comments