/ docs / ssd1677-driver.md
ssd1677-driver.md
  1  # SSD1677 E-Ink Display Driver Guide
  2  
  3  ## GDEQ0426T82 (4.26" 800x480) on Xteink X4
  4  
  5  Complete reference for programming the SSD1677 e-paper display controller, including initialization, image updates, custom LUT creation, and low-level protocol details.
  6  
  7  Based on the GxEPD2_426_GDEQ0426T82 driver implementation.
  8  
  9  ---
 10  
 11  ## Table of Contents
 12  
 13  - [Hardware Configuration](#hardware-configuration)
 14  - [SPI Communication](#spi-communication)
 15  - [Initialization](#initialization)
 16  - [RAM Operations](#ram-operations)
 17  - [Writing Image Data](#writing-image-data)
 18  - [Display Updates](#display-updates)
 19  - [Custom LUT Guide](#custom-lut-guide)
 20  - [Complete Workflows](#complete-workflows)
 21  - [Command Reference](#command-reference)
 22  - [Timing & Power Management](#timing--power-management)
 23  
 24  ---
 25  
 26  ## Hardware Configuration
 27  
 28  - **Controller**: SSD1677
 29  - **Display**: 800x480 pixels (100x60 bytes = 48,000 bytes)
 30  - **SPI Pins**: SCLK=8, MOSI=10, CS=21, DC=4, RST=5, BUSY=6
 31  - **SPI Settings**: 40MHz (spec: 20MHz, but 40MHz works), MSB First, SPI Mode 0
 32  
 33  ---
 34  
 35  ## SPI Communication
 36  
 37  ### Low-Level Protocol
 38  
 39  **Command Format:**
 40  ```
 41  DC=LOW, CS=LOW -> Transfer(command_byte) -> CS=HIGH, DC=HIGH
 42  ```
 43  
 44  **Data Format:**
 45  ```
 46  DC=HIGH, CS=LOW -> Transfer(data_byte) -> CS=HIGH
 47  ```
 48  
 49  **Bulk Transfer Format:**
 50  ```
 51  CS=LOW -> Transfer(byte1) -> Transfer(byte2) -> ... -> CS=HIGH
 52  ```
 53  
 54  ### Reset Sequence
 55  
 56  ```
 57  RST=HIGH -> delay(20ms) -> RST=LOW -> delay(2ms) -> RST=HIGH -> delay(20ms)
 58  ```
 59  
 60  ---
 61  
 62  ## Initialization
 63  
 64  ### Minimal Initialization Example
 65  
 66  ```c
 67  void ssd1677_init() {
 68      // 1. Software Reset
 69      epd_cmd(0x12);        // SWRESET
 70      epd_wait_busy();      // Wait for busy to clear
 71  
 72      // 2. Driver Output Control
 73      epd_cmd(0x01);
 74      epd_data(0xA7);       // 680 rows -> 0x2A7, low byte
 75      epd_data(0x02);       // high byte
 76      epd_data(0x00);       // GD=0, SM=0, TB=0
 77  
 78      // 3. Data Entry Mode
 79      epd_cmd(0x11);
 80      epd_data(0x03);       // X+, Y+
 81  
 82      // 4. RAM X Start/End
 83      epd_cmd(0x44);
 84      epd_data(0x00);       // X start = 0
 85      epd_data(0x3B);       // X end = 959 / 8 = 0x3B
 86  
 87      // 5. RAM Y Start/End
 88      epd_cmd(0x45);
 89      epd_data(0x00);       // Y start (low byte)
 90      epd_data(0x00);       // Y start (high byte)
 91      epd_data(0xA7);       // Y end (low byte)
 92      epd_data(0x02);       // Y end (high byte)
 93  
 94      // 6. Border Control
 95      epd_cmd(0x3C);
 96      epd_data(0xC0);       // Border = Hi-Z
 97  
 98      // 7. Temperature Sensor (internal)
 99      epd_cmd(0x18);
100      epd_data(0x80);
101  }
102  ```
103  
104  ### Detailed Initialization Sequence
105  
106  After reset:
107  
108  1. **0x12** — Software Reset (SWRESET), then wait BUSY
109  2. **0x18** `0x80` — Temperature sensor control (internal)
110  3. **0x0C** `0xAE, 0xC7, 0xC3, 0xC0, 0x40` — Booster soft start configuration
111  4. **0x01** `0xDF, 0x01, 0x02` — Driver output control (479 gates = HEIGHT-1)
112  5. **0x3C** `0x01` — Border waveform control
113  6. Set RAM area (see below)
114  7. **0x46** `0xF7` — Auto write BW RAM (clear to white), then wait BUSY
115  8. **0x47** `0xF7` — Auto write RED RAM (clear to white), then wait BUSY
116  
117  ### Command Explanations
118  
119  **Software Reset (0x12)** — Resets the internal registers (except deep sleep). Mandatory after power-up.
120  
121  **Driver Output Control (0x01)** — Sets display height and scan direction:
122  - Byte 1: `(HEIGHT - 1) % 256` = `0xDF` (479 & 0xFF)
123  - Byte 2: `(HEIGHT - 1) / 256` = `0x01` (479 >> 8)
124  - Byte 3: `0x02` (interlaced/SM mode)
125  
126  **Data Entry Mode (0x11)** — Controls RAM addressing: `0x03 = X increment, Y increment`.
127  
128  **Set RAM Window (0x44 & 0x45)** — Defines the region written during RAM writes. For full 960x680 screen, X=0..0x3B, Y=0..0x2A7.
129  
130  **Border Waveform (0x3C)** — Controls VBD (border pixel behavior). `0xC0 = Hi-Z`, common default.
131  
132  **Temperature Sensor (0x18)** — `0x80 = use internal sensor`.
133  
134  ---
135  
136  ## RAM Operations
137  
138  ### RAM Area Configuration
139  
140  Sets the window for subsequent RAM writes. Y-coordinates are reversed due to hardware gates orientation.
141  
142  **Important:** X addresses are specified in **pixels**, not bytes. The controller handles the byte conversion internally.
143  
144  **For coordinates (x, y, w, h):**
145  
146  1. Calculate reversed Y: `y_rev = HEIGHT - y - h`
147  
148  2. **Set RAM Entry Mode** (Command `0x11`): Data `0x01` (X increment, Y decrement - Y reversed)
149  
150  3. **Set RAM X Address** (Command `0x44`) — In pixels:
151     - Data[0]: `x % 256` (X start LSB)
152     - Data[1]: `x / 256` (X start MSB)
153     - Data[2]: `(x + w - 1) % 256` (X end LSB)
154     - Data[3]: `(x + w - 1) / 256` (X end MSB)
155  
156  4. **Set RAM Y Address** (Command `0x45`):
157     - Data[0]: `(y_rev + h - 1) % 256` (Y start LSB)
158     - Data[1]: `(y_rev + h - 1) / 256` (Y start MSB)
159     - Data[2]: `y_rev % 256` (Y end LSB)
160     - Data[3]: `y_rev / 256` (Y end MSB)
161  
162  5. **Set RAM X Counter** (Command `0x4E`):
163     - Data[0]: `x % 256` (Initial X LSB)
164     - Data[1]: `x / 256` (Initial X MSB)
165  
166  6. **Set RAM Y Counter** (Command `0x4F`):
167     - Data[0]: `(y_rev + h - 1) % 256` (Initial Y LSB)
168     - Data[1]: `(y_rev + h - 1) / 256` (Initial Y MSB)
169  
170  ---
171  
172  ## Writing Image Data
173  
174  ### Write to Current Buffer (Command 0x24)
175  
176  ```c
177  void ssd1677_write_bw(uint8_t *buffer, uint32_t size) {
178      // Set RAM Address Counters
179      epd_cmd(0x4E);     // X counter
180      epd_data(0x00);
181      epd_cmd(0x4F);     // Y counter
182      epd_data(0x00);
183      epd_data(0x00);
184  
185      // Write BW RAM
186      epd_cmd(0x24);
187      for (uint32_t i = 0; i < size; i++)
188          epd_data(buffer[i]);
189  }
190  ```
191  
192  **Process:**
193  1. Configure RAM area with `_setPartialRamArea(x, y, w, h)`
194  2. Send command `0x24`
195  3. Start bulk transfer (CS=LOW)
196  4. Transfer image data bytes (one bit per pixel, MSB first)
197     - Total bytes = `(w * h) / 8`
198     - `0xFF` = white, `0x00` = black
199  5. End transfer (CS=HIGH)
200  
201  **Explanation:**
202  - **0x4E / 0x4F** set starting address for RAM
203  - **0x24** selects the BW image buffer
204  
205  ### Write to Previous Buffer (Command 0x26)
206  
207  Same as above but use command `0x26` instead of `0x24`. Used for differential updates.
208  
209  ### Full Screen Clear
210  
211  1. Write to previous buffer: `_setPartialRamArea(0, 0, 800, 480)` -> Command `0x26` -> 48000 bytes of `0xFF`
212  2. Write to current buffer: `_setPartialRamArea(0, 0, 800, 480)` -> Command `0x24` -> 48000 bytes of `0xFF`
213  3. Perform full refresh
214  
215  ### Full Frame Example
216  
217  ```c
218  void ssd1677_display_frame(uint8_t *bw, uint8_t *red) {
219      ssd1677_write_bw(bw, BW_BUFFER_SIZE);
220  
221      epd_cmd(0x26);      // Write RED RAM
222      for (int i = 0; i < RED_BUFFER_SIZE; i++)
223          epd_data(red[i]);
224  
225      ssd1677_update();
226  }
227  ```
228  
229  ---
230  
231  ## Display Updates
232  
233  ### Power On
234  
235  - **0x22** `0xE0` — Display update control sequence
236  - **0x20** — Master activation (trigger update)
237  - Wait ~100ms while BUSY pin is HIGH
238  
239  ### Full Refresh
240  
241  ```c
242  void ssd1677_update() {
243      epd_cmd(0x22);
244      epd_data(0xC7);   // Display mode: load LUT + refresh + power off
245  
246      epd_cmd(0x20);    // Master activation
247      epd_wait_busy();  // Wait for driving waves to complete
248  }
249  ```
250  
251  **Detailed Sequence:**
252  
253  1. **0x21** `0x40, 0x00` — Display update control (bypass RED as 0, single chip)
254  2. For fast mode: **0x1A** `0x5A` (temperature register), then **0x22** `0xD7`
255  3. For normal mode: **0x22** `0xF7` (extended temp)
256  4. **0x20** — Master activation
257  5. Wait ~1600ms while BUSY pin is HIGH
258  
259  **Explanation:**
260  - **0x22 / 0xC7** tells SSD1677 which tasks to run (enable analog, load LUT, drive display)
261  - **0x20** starts the entire update cycle
262  - **epd_wait_busy()** waits until the driver finishes waveform driving
263  
264  **Fast vs Normal Mode**: `useFastFullUpdate=true` uses faster refresh but limited temperature range.
265  
266  ### Display Update Control 2 (0x22) Bit Documentation
267  
268  Based on driver implementation analysis:
269  
270  - **Bit 7 (0x80)** CLOCK_ON — Start internal oscillator
271  - **Bit 6 (0x40)** ANALOG_ON — Enable analog power rails (VGH/VGL drivers)
272  - **Bit 5 (0x20)** TEMP_LOAD — Load temperature (internal or external)
273  - **Bit 4 (0x10)** LUT_LOAD — Load waveform LUT
274  - **Bit 3 (0x08)** MODE_SELECT — Mode 1/2 selection
275  - **Bit 2 (0x04)** DISPLAY_START — Run display update
276  - **Bit 1 (0x02)** ANALOG_OFF — Analog shutdown phase
277  - **Bit 0 (0x01)** CLOCK_OFF — Disable internal oscillator
278  
279  **Common Patterns:**
280  - Full refresh (first power on): `0xC0 | 0x34 = 0xF4` (CLOCK+ANALOG+TEMP+LUT+DISPLAY)
281  - Full refresh (already on): `0x34` (TEMP+LUT+DISPLAY)
282  - Half refresh with high temp: `0xD4` (CLOCK+ANALOG+LUT+DISPLAY)
283  - Fast refresh with custom LUT: `0x0C` (MODE+DISPLAY)
284  - Fast refresh without custom LUT: `0x1C` (LUT+MODE+DISPLAY)
285  
286  ### Partial Refresh
287  
288  - **0x21** `0x00, 0x00` — Display update control (RED normal, single chip)
289  - **0x22** `0xFC` — Partial update sequence
290  - **0x20** — Master activation
291  - Wait ~600ms while BUSY pin is HIGH
292  
293  ---
294  
295  ## Custom LUT Guide
296  
297  ### What is a LUT?
298  
299  The SSD1677 uses a **Look-Up Table (LUT)** to control **pixel waveform driving** during updates. Each pixel (BW/RED) needs a sequence of voltage phases to switch states correctly.
300  
301  A LUT controls:
302  - Voltage level per phase (VSH1, VSH2, VSL, Hi-Z)
303  - VCOM toggling pattern
304  - Duration of each phase (TP0-TP7)
305  - Phase repetitions
306  - Additional red-pixel handling
307  
308  ### LUT Structure (111 bytes)
309  
310  Used in the driver implementation for grayscale support:
311  
312  - **Bytes 0-49** (50 bytes) — VS waveforms (5 groups x 10 bytes)
313  - **Bytes 50-99** (50 bytes) — TP/RP timing groups (10 groups x 5 bytes)
314  - **Bytes 100-104** (5 bytes) — Frame rate control
315  - **Byte 105** — VGH (Gate voltage) - sent via 0x03
316  - **Byte 106** — VSH1 (Source voltage 1) - sent via 0x04
317  - **Byte 107** — VSH2 (Source voltage 2) - sent via 0x04
318  - **Byte 108** — VSL (Source voltage low) - sent via 0x04
319  - **Byte 109** — VCOM voltage - sent via 0x2C
320  - **Byte 110** — Reserved
321  
322  **Note:** Bytes 105-109 are sent using separate voltage control commands after loading the main LUT.
323  
324  ### How to Build a Custom LUT
325  
326  **Step 1 — Define Source Voltage Waveform (WS0-WS7)**
327  
328  You choose for each phase:
329  - VSH1 (medium positive)
330  - VSH2 (strong positive - drives white)
331  - VSL (strong negative - drives black)
332  - Hi-Z (float)
333  
334  These define **pixel movement direction** and strength.
335  
336  **Step 2 — Define VCOM Waveform (WS8-WS14)**
337  
338  VCOM biases the entire display. These bytes define:
339  - On/off toggling per phase
340  - Matching with source driver phases
341  - Ghost reduction
342  
343  **Step 3 — Phase Timing TP0-TP7 (WS15-WS23)**
344  
345  Each TPx sets duration of a phase. Longer = cleaner image, slower refresh. Shorter = faster, but potential ghosting.
346  
347  **Step 4 — Repeat Counts & Finalization (WS24-WS33)**
348  
349  These adjust:
350  - How many times each phase repeats
351  - Red pigment extra driving
352  - Cleanup phases
353  
354  ### How to Load a Custom LUT
355  
356  A custom LUT is written using **Command 0x32**:
357  
358  ```
359  CMD 0x32
360  DATA WS0
361  DATA WS1
362  ...
363  DATA WS33
364  ```
365  
366  The first **105 bytes** are written to the LUT register (0x32), followed by separate voltage control commands.
367  
368  ```c
369  // Load LUT (111-byte format with voltage controls)
370  void ssd1677_load_lut_extended(const uint8_t* lut) {
371      // Load main LUT (105 bytes: VS + TP/RP + frame rate)
372      epd_cmd(0x32);
373      for (int i = 0; i < 105; i++)
374          epd_data(lut[i]);
375  
376      // Set voltage values from bytes 105-109
377      epd_cmd(0x03);  // Gate voltage (VGH)
378      epd_data(lut[105]);
379  
380      epd_cmd(0x04);  // Source voltages (VSH1, VSH2, VSL)
381      epd_data(lut[106]);  // VSH1
382      epd_data(lut[107]);  // VSH2
383      epd_data(lut[108]);  // VSL
384  
385      epd_cmd(0x2C);  // VCOM voltage
386      epd_data(lut[109]);
387  }
388  ```
389  
390  ### How to Apply (Use) the Custom LUT
391  
392  After loading the LUT, tell the display to **use it**.
393  
394  **1. Configure Display Update Mode (0x22)**
395  
396  Typical value enabling LUT usage:
397  ```
398  CMD 0x22
399  DATA 0xF7
400  ```
401  
402  **2. Start Master Activation (0x20)**
403  
404  ```
405  CMD 0x20
406  WAIT BUSY = LOW
407  ```
408  
409  While BUSY is high, the LUT waveform is driving the display.
410  
411  ```c
412  // Apply LUT
413  void ssd1677_apply_lut() {
414      epd_cmd(0x22);
415      epd_data(0xF7);   // Use LUT
416      epd_cmd(0x20);    // Master Activation
417      epd_wait_busy();
418  }
419  ```
420  
421  ### LUT Summary
422  
423  **Build a custom LUT** — Create 111 bytes: 105 for LUT register + 5 voltage values + 1 reserved
424  
425  **Use a custom LUT:**
426  1. Write with **0x32**
427  2. Enable with **0x22**
428  3. Trigger with **0x20**
429  
430  **Optional** — Burn to OTP with **0x36**
431  
432  ### Grayscale Rendering with Custom LUTs
433  
434  The driver implements 4-level grayscale using a multi-pass technique with custom LUTs.
435  
436  **Grayscale Principle:**
437  
438  1. **First pass (Black/White):** Write BW framebuffer to both RAM buffers, perform standard refresh
439  2. **Second pass (Grayscale):** Write LSB and MSB grayscale buffers, apply custom grayscale LUT, perform fast refresh
440  3. The custom LUT creates intermediate gray levels by controlling pixel voltage phases
441  
442  **Grayscale LUT Structure:**
443  
444  The driver includes two grayscale LUTs (111 bytes each):
445  - `lut_grayscale`: Forward grayscale rendering
446  - `lut_grayscale_revert`: Cleans up grayscale artifacts back to pure BW
447  
448  Key characteristics:
449  - Uses different voltage sequences for 4 gray levels (00, 01, 10, 11)
450  - Frame timing optimized for fast refresh (~500ms)
451  - VS waveforms: 50 bytes (5 groups x 10 bytes)
452  - TP/RP timing: 50 bytes (10 groups x 5 bytes)
453  - Voltages: VGH=0x17, VSH1=0x41, VSH2=0xA8, VSL=0x32, VCOM=0x30
454  
455  ---
456  
457  ## Complete Workflows
458  
459  ### Full Screen Update (Initial or Complete Refresh)
460  
461  ```
462  1. _InitDisplay() [if not initialized]
463  2. _setPartialRamArea(0, 0, 800, 480)
464  3. Write to previous buffer: Command 0x26 + 48000 bytes
465  4. Write to current buffer: Command 0x24 + 48000 bytes
466  5. _Update_Full()
467  ```
468  
469  ### Partial Update (Fast Refresh)
470  
471  ```
472  1. _InitDisplay() [if not initialized]
473  2. [First time only] Clear screen buffers with full refresh
474  3. _setPartialRamArea(x, y, w, h)
475  4. Write image: Command 0x24 + image bytes
476  5. _Update_Part()
477  6. Write image again: Command 0x24 + image bytes
478  7. Write to previous: Command 0x26 + same image bytes
479  ```
480  
481  **Why write twice?** Partial updates compare current vs previous buffer. Writing to both buffers after refresh prevents ghosting on next update.
482  
483  ### Minimal Usage Example
484  
485  ```c
486  ssd1677_init();
487  
488  ssd1677_display_frame(bw_image, red_image);
489  ```
490  
491  ### Complete Example: Fast Refresh with Double Buffering
492  
493  The driver implements double buffering to enable fast partial updates:
494  
495  ```cpp
496  // Initialize display
497  display.begin();
498  display.clearScreen(0xFF);
499  display.displayBuffer(FULL_REFRESH);
500  
501  // Draw content to framebuffer
502  uint8_t* fb = display.getFrameBuffer();
503  // ... draw into fb ...
504  
505  // Fast refresh (compares with previous frame)
506  display.displayBuffer(FAST_REFRESH);
507  
508  // Next frame
509  // ... modify fb ...
510  display.displayBuffer(FAST_REFRESH);
511  ```
512  
513  **How it works:**
514  1. Two internal buffers (`frameBuffer0` and `frameBuffer1`) alternate as current/previous
515  2. On `displayBuffer()`, current buffer written to BW RAM (0x24), previous to RED RAM (0x26)
516  3. Controller compares buffers and only updates changed pixels
517  4. Buffers swap roles after each display
518  
519  ### Auto-Write Commands for Fast Clear
520  
521  Commands `0x46` and `0x47` allow rapid buffer clearing:
522  
523  ```c
524  // Clear BW RAM to white pattern
525  sendCommand(0x46);  // Auto write BW RAM
526  sendData(0xF7);     // Fill pattern
527  waitWhileBusy();
528  
529  // Clear RED RAM to white pattern
530  sendCommand(0x47);  // Auto write RED RAM
531  sendData(0xF7);     // Fill pattern
532  waitWhileBusy();
533  ```
534  
535  This is much faster than writing 48,000 bytes manually during initialization.
536  
537  ---
538  
539  ## Command Reference
540  
541  - **0x01** Driver Output Control — Set gate scanning (HEIGHT)
542  - **0x03** Gate Voltage — Set VGH voltage level
543  - **0x04** Source Voltage — Set VSH1, VSH2, VSL voltages
544  - **0x0C** Booster Soft Start — Configure boost converter
545  - **0x10** Deep Sleep Mode — Enter low power mode
546  - **0x11** Data Entry Mode — Set X/Y increment direction
547  - **0x12** Software Reset — Reset controller
548  - **0x18** Temperature Sensor — Control temp sensor
549  - **0x1A** Temperature Register — Set temp value (fast mode)
550  - **0x20** Master Activation — Trigger display update
551  - **0x21** Display Update Control — Configure update mode
552  - **0x22** Display Update Sequence — Set update waveform
553  - **0x24** Write RAM (BW) — Write to current buffer
554  - **0x26** Write RAM (RED/OLD) — Write to previous buffer
555  - **0x2C** Write VCOM — Set VCOM voltage
556  - **0x32** Write LUT Register — Load custom 105-byte LUT (part of 111-byte structure)
557  - **0x36** Write OTP — Burn LUT to one-time-programmable memory
558  - **0x3C** Border Waveform — Configure border behavior
559  - **0x44** Set RAM X Address — Define X window (in pixels)
560  - **0x45** Set RAM Y Address — Define Y window (in pixels)
561  - **0x46** Auto Write BW RAM — Fast fill BW RAM with pattern
562  - **0x47** Auto Write RED RAM — Fast fill RED RAM with pattern
563  - **0x4E** Set RAM X Counter — Set initial X position (in pixels)
564  - **0x4F** Set RAM Y Counter — Set initial Y position (in pixels)
565  
566  ---
567  
568  ## Timing & Power Management
569  
570  ### Timing Specifications
571  
572  - **Reset pulse** — 10ms low duration
573  - **Power on** — ~100ms BUSY signal duration
574  - **Power off** — ~200ms BUSY signal duration
575  - **Full refresh** — ~1600ms normal mode, wait for BUSY
576  - **Partial refresh** — ~600ms, wait for BUSY
577  - **Software reset delay** — 10ms after command 0x12
578  
579  ### BUSY Signal Monitoring
580  
581  - **Pin**: GPIO6 (INPUT)
582  - **Active level**: HIGH
583  - **Polling**: Read pin until LOW, with timeout protection
584  - **Timeout**: 10000ms (10 seconds)
585  - **Usage**: Wait after commands `0x20` (master activation)
586  
587  ### Power Off
588  
589  - **0x22** `0x83` — Power off sequence
590  - **0x20** — Master activation
591  - Wait ~200ms while BUSY pin is HIGH
592  
593  ### Hibernate (Deep Sleep)
594  
595  1. Execute Power Off sequence
596  2. Send command `0x10` (Deep Sleep Mode)
597  3. Send data `0x01` (Enter deep sleep)
598  
599  **Wake from Hibernate**: Requires hardware reset via RST pin.
600  
601  ---
602  
603  ## Sunlight Fading Issue
604  
605  ### Problem
606  
607  The XTEINK X4's SSD1677 display driver IC is packaged as "Gold Bump Die" without resin protection. This makes the IC susceptible to UV radiation. In bright sunlight, this causes the screen to fade to white.
608  
609  White X4 devices are more affected than black ones due to lower UV absorption by the case.
610  
611  ### Solution
612  
613  Power down the display's VBUS after rendering each page. This is done by setting the analog shutdown bits in the Display Update Control 2 command (0x22):
614  
615  ```c
616  // After refresh, power down display to prevent UV fading
617  sendCommand(0x22);
618  sendData(displayMode | 0x03);  // Set ANALOG_OFF_PHASE (bit 1) and CLOCK_OFF (bit 0)
619  sendCommand(0x20);
620  waitWhileBusy();
621  ```
622  
623  The firmware implements this as the "Sunlight Fading Fix" setting in Device Settings. When enabled:
624  - Sets bits 0 and 1 in the 0x22 command after each refresh
625  - Adds ~100-200ms overhead per page turn (power-on cycle)
626  - Screen will power back on automatically for the next refresh
627  
628  This fix was developed by the crosspoint-reader community.
629  
630  ### Physical Alternative
631  
632  For permanent protection, apply UV-blocking tape over the driver IC area on the display PCB.
633  
634  ---
635  
636  ## Important Notes
637  
638  - BUSY pin *must* be polled after reset and update
639  - All RAM writes auto-increment based on data entry mode
640  - SSD1677 can display BW-only or RED-only if desired
641  - All X coordinates and widths must be multiples of 8 (byte boundaries)
642  - Y coordinates are reversed in hardware (gates bottom-to-top)
643  - RAM auto-increments after each byte transfer
644  - Total RAM size: 48,000 bytes (800x480 / 8)
645  - Dual-buffer system enables differential partial updates
646  - First write after init should be full refresh to clear ghost images
647  - In sunlight: enable "Sunlight Fading Fix" setting to prevent UV-induced screen fading