Dave Allie af965a074b
[EInkDisplay] Single buffer mode (#7)
* Cleanup EInkDisplay

* Add EINK_DISPLAY_SINGLE_BUFFER_MODE build flag and allow for single buffer rendering

* Add SSD1677 E-Ink Display Driver Guide

Copied verbatium from https://github.com/CidVonHighwind/microreader/blob/main/doc/SSD1677_GUIDE.md

* Add a few details in the readme and update the SSD1677_GUIDE
2025-12-16 22:43:29 +11:00

25 KiB
Raw Blame History

SSD1677 E-Ink Display Driver Guide

GDEQ0426T82 (4.26" 800×480) on Xteink X4

Note: This document was AI-generated by consolidating multiple reference documents. Please verify critical details against the official SSD1677 datasheet.

Complete reference for programming the SSD1677 e-paper display controller, including initialization, image updates, custom LUT creation, and low-level protocol details.

Based on the GxEPD2_426_GDEQ0426T82 driver implementation.


Table of Contents


Hardware Configuration

  • Controller: SSD1677
  • Display: 800×480 pixels (100×60 bytes = 48,000 bytes)
  • SPI Pins: SCLK=8, MOSI=10, CS=21, DC=4, RST=5, BUSY=6
  • SPI Settings: 40MHz (spec: 20MHz, but 40MHz works), MSB First, SPI Mode 0

Buffer Modes

The EInkDisplay driver supports two buffering modes, selectable at compile time:

Dual Buffer Mode (Default)

Memory usage: 96KB (two 48KB framebuffers)

  • Two framebuffers in ESP32 RAM: frameBuffer0 and frameBuffer1
  • Buffers alternate roles as "current" and "previous" using swapBuffers()
  • On each displayBuffer():
    • Current buffer → BW RAM (0x24)
    • Previous buffer → RED RAM (0x26)
    • Display controller compares them for differential fast refresh
    • Buffers swap roles
  • Advantage: Fast buffer switching with no overhead
  • Disadvantage: Uses 96KB of precious RAM

Single Buffer Mode (Memory Optimized)

Memory usage: 48KB (one 48KB framebuffer)

Enable by defining EINK_DISPLAY_SINGLE_BUFFER_MODE before including EInkDisplay.h:

#define EINK_DISPLAY_SINGLE_BUFFER_MODE
#include <EInkDisplay.h>

Or in your build system (e.g., platformio.ini):

build_flags =
    -D EINK_DISPLAY_SINGLE_BUFFER_MODE
  • Single framebuffer in ESP32 RAM: frameBuffer0
  • Display's internal RED RAM acts as "previous frame" storage
  • On each displayBuffer():
    • FAST_REFRESH:
      • New frame → BW RAM (0x24)
      • Display refresh (compares BW vs existing RED RAM)
      • New frame → RED RAM (0x26) - syncs for next refresh
    • HALF/FULL_REFRESH:
      • New frame → both BW RAM and RED RAM (0x24 and 0x26)
      • Display refresh
      • Extra RED RAM sync (already contains correct frame)
  • Advantages:
    • Saves 48KB RAM (critical for ESP32-C3 with ~380KB usable)
    • Fast refresh still works via differential updates
  • Disadvantages:
    • Extra RED RAM write after each refresh (~100ms overhead)
    • Grayscale rendering requires temporary buffer allocation
    • swapBuffers() not available (not needed)

Choosing a Mode

Use Dual Buffer Mode if:

  • RAM is plentiful
  • You want absolute minimum latency
  • You don't need every KB of RAM

Use Single Buffer Mode if:

  • Running on memory-constrained hardware (like ESP32-C3)
  • 48KB RAM savings is critical for your application
  • ~100ms extra per refresh is acceptable

API Differences Between Modes

Most of the API is identical between modes, but there are a few differences:

Feature Dual Buffer Single Buffer
getFrameBuffer() ✓ Available ✓ Available
clearScreen() ✓ Available ✓ Available
displayBuffer() ✓ Available ✓ Available
swapBuffers() ✓ Available ✗ Not available
cleanupGrayscaleBuffers() ✗ Not needed ✓ Required after grayscale
Memory overhead 96KB always 48KB + temp 48KB during grayscale

Code that works in both modes:

display.begin();
display.clearScreen();
display.displayBuffer(FAST_REFRESH);

Code specific to single buffer mode:

(This is not required in dual buffer mode)

// Before grayscale
const auto bwBuffer = static_cast<uint8_t *>(malloc(EInkDisplay::BUFFER_SIZE));
memcpy(bwBuffer, display.getFrameBuffer(), EInkDisplay::BUFFER_SIZE);

// ... grayscale rendering ...

// After grayscale
display.cleanupGrayscaleBuffers(bwBuffer);
free(bwBuffer);      

SPI Communication

Low-Level Protocol

Command Format

DC=LOW, CS=LOW → Transfer(command_byte) → CS=HIGH, DC=HIGH

Data Format

DC=HIGH, CS=LOW → Transfer(data_byte) → CS=HIGH

Bulk Transfer Format

CS=LOW → Transfer(byte1) → Transfer(byte2) → ... → CS=HIGH

Reset Sequence

RST=HIGH → delay(20ms) → RST=LOW → delay(2ms) → RST=HIGH → delay(20ms)

Initialization

Minimal Initialization Example

void ssd1677_init() {
    // 1. Software Reset
    epd_cmd(0x12);        // SWRESET
    epd_wait_busy();      // Wait for busy to clear

    // 2. Driver Output Control
    epd_cmd(0x01);
    epd_data(0xA7);       // 680 rows -> 0x2A7, low byte
    epd_data(0x02);       // high byte
    epd_data(0x00);       // GD=0, SM=0, TB=0

    // 3. Data Entry Mode
    epd_cmd(0x11);
    epd_data(0x03);       // X+, Y+

    // 4. RAM X Start/End
    epd_cmd(0x44);
    epd_data(0x00);       // X start = 0
    epd_data(0x3B);       // X end = 959 / 8 = 0x3B

    // 5. RAM Y Start/End
    epd_cmd(0x45);
    epd_data(0x00);       // Y start (low byte)
    epd_data(0x00);       // Y start (high byte)
    epd_data(0xA7);       // Y end (low byte)
    epd_data(0x02);       // Y end (high byte)

    // 6. Border Control
    epd_cmd(0x3C);
    epd_data(0xC0);       // Border = Hi-Z

    // 7. Temperature Sensor (internal)
    epd_cmd(0x18);
    epd_data(0x80);
}

Detailed Initialization Sequence

After reset, then:

Step Command Data Description
1 0x12 - Software Reset (SWRESET)
- Wait BUSY - Wait for reset to complete
2 0x18 0x80 Temperature sensor control (internal)
3 0x0C 0xAE, 0xC7, 0xC3, 0xC0, 0x40 Booster soft start configuration
4 0x01 0xDF, 0x01, 0x02 Driver output control (479 gates = HEIGHT-1)
5 0x3C 0x01 Border waveform control
6 Set RAM area See below Configure full screen area
7 0x46 0xF7 Auto write BW RAM (clear to white)
8 Wait BUSY - Wait for auto-write to complete
9 0x47 0xF7 Auto write RED RAM (clear to white)
10 Wait BUSY - Wait for auto-write to complete

Command Explanations

Software Reset (0x12)
Resets the internal registers (except deep sleep). Mandatory after power-up.

Driver Output Control (0x01)
Sets display height and scan direction.

  • Byte 1: (HEIGHT - 1) % 256 = 0xDF (479 & 0xFF)
  • Byte 2: (HEIGHT - 1) / 256 = 0x01 (479 >> 8)
  • Byte 3: 0x02 (interlaced/SM mode)

Data Entry Mode (0x11)
Controls RAM addressing: 0x03 = X increment, Y increment.

Set RAM Window (0x44 & 0x45)
Defines the region written during RAM writes.
For full 960×680 screen, X=0..0x3B, Y=0..0x2A7.

Border Waveform (0x3C)
Controls VBD (border pixel behavior). 0xC0 = Hi-Z, common default.

Temperature Sensor (0x18)
0x80 = use internal sensor.


RAM Operations

RAM Area Configuration

Sets the window for subsequent RAM writes. Y-coordinates are reversed due to hardware gates orientation.

Important: X addresses are specified in pixels, not bytes. The controller handles the byte conversion internally.

For coordinates (x, y, w, h):

  1. Calculate reversed Y: y_rev = HEIGHT - y - h

  2. Set RAM Entry Mode (Command 0x11):

    • Data: 0x01 (X increment, Y decrement - Y reversed)
  3. Set RAM X Address (Command 0x44) - In pixels:

    • Data[0]: x % 256 (X start LSB)
    • Data[1]: x / 256 (X start MSB)
    • Data[2]: (x + w - 1) % 256 (X end LSB)
    • Data[3]: (x + w - 1) / 256 (X end MSB)
  4. Set RAM Y Address (Command 0x45):

    • Data[0]: (y_rev + h - 1) % 256 (Y start LSB)
    • Data[1]: (y_rev + h - 1) / 256 (Y start MSB)
    • Data[2]: y_rev % 256 (Y end LSB)
    • Data[3]: y_rev / 256 (Y end MSB)
  5. Set RAM X Counter (Command 0x4E):

    • Data[0]: x % 256 (Initial X LSB)
    • Data[1]: x / 256 (Initial X MSB)
  6. Set RAM Y Counter (Command 0x4F):

    • Data[0]: (y_rev + h - 1) % 256 (Initial Y LSB)
    • Data[1]: (y_rev + h - 1) / 256 (Initial Y MSB)

Writing Image Data

Write to Current Buffer (Command 0x24)

void ssd1677_write_bw(uint8_t *buffer, uint32_t size) {
    // Set RAM Address Counters
    epd_cmd(0x4E);     // X counter
    epd_data(0x00);
    epd_cmd(0x4F);     // Y counter
    epd_data(0x00);
    epd_data(0x00);

    // Write BW RAM
    epd_cmd(0x24);
    for (uint32_t i = 0; i < size; i++)
        epd_data(buffer[i]);
}

Process:

  1. Configure RAM area with _setPartialRamArea(x, y, w, h)
  2. Send command 0x24
  3. Start bulk transfer (CS=LOW)
  4. Transfer image data bytes (one bit per pixel, MSB first)
    • Total bytes = (w * h) / 8
    • 0xFF = white, 0x00 = black
  5. End transfer (CS=HIGH)

Explanation:

  • 0x4E / 0x4F set starting address for RAM.
  • 0x24 selects the BW image buffer.

Write to Previous Buffer (Command 0x26)

Same as above but use command 0x26 instead of 0x24. Used for differential updates.

Full Screen Clear

  1. Write to previous buffer: _setPartialRamArea(0, 0, 800, 480) → Command 0x26 → 48000 bytes of 0xFF
  2. Write to current buffer: _setPartialRamArea(0, 0, 800, 480) → Command 0x24 → 48000 bytes of 0xFF
  3. Perform full refresh

Full Frame Example

void ssd1677_display_frame(uint8_t *bw, uint8_t *red) {
    ssd1677_write_bw(bw, BW_BUFFER_SIZE);

    epd_cmd(0x26);      // Write RED RAM
    for (int i = 0; i < RED_BUFFER_SIZE; i++)
        epd_data(red[i]);

    ssd1677_update();
}

Display Updates

Power On

Command Data Description
0x22 0xE0 Display update control sequence
0x20 - Master activation (trigger update)
Wait ~100ms Wait while BUSY pin is HIGH

Full Refresh

void ssd1677_update() {
    epd_cmd(0x22);
    epd_data(0xC7);   // Display mode: load LUT + refresh + power off

    epd_cmd(0x20);    // Master activation
    epd_wait_busy();  // Wait for driving waves to complete
}

Detailed Sequence:

Step Command Data Description
1 0x21 0x40, 0x00 Display update control (bypass RED as 0, single chip)
2a 0x1A 0x5A Temperature register (fast mode only)
2b 0x22 0xD7 Update sequence (fast mode)
OR
2b 0x22 0xF7 Update sequence (normal mode, extended temp)
3 0x20 - Master activation
Wait ~1600ms Wait while BUSY pin is HIGH

Explanation:

  • 0x22 / 0xC7 tells SSD1677 which tasks to run (enable analog, load LUT, drive display).
  • 0x20 starts the entire update cycle.
  • epd_wait_busy() waits until the driver finishes waveform driving.

Fast vs Normal Mode: useFastFullUpdate=true uses faster refresh but limited temperature range.

Display Update Control 2 (0x22) Bit Documentation

Based on driver implementation analysis:

Bit Hex Name Effect
7 0x80 CLOCK_ON Start internal oscillator
6 0x40 ANALOG_ON Enable analog power rails (VGH/VGL drivers)
5 0x20 TEMP_LOAD Load temperature (internal or external)
4 0x10 LUT_LOAD Load waveform LUT
3 0x08 MODE_SELECT Mode 1/2 selection
2 0x04 DISPLAY_START Run display update
1 0x02 ANALOG_OFF Analog shutdown phase
0 0x01 CLOCK_OFF Disable internal oscillator

Common Patterns:

  • Full refresh (first power on): 0xC0 | 0x34 = 0xF4 (CLOCK+ANALOG+TEMP+LUT+DISPLAY)
  • Full refresh (already on): 0x34 (TEMP+LUT+DISPLAY)
  • Half refresh with high temp: 0xD4 (CLOCK+ANALOG+LUT+DISPLAY)
  • Fast refresh with custom LUT: 0x0C (MODE+DISPLAY)
  • Fast refresh without custom LUT: 0x1C (LUT+MODE+DISPLAY)

Partial Refresh

Command Data Description
0x21 0x00, 0x00 Display update control (RED normal, single chip)
0x22 0xFC Partial update sequence
0x20 - Master activation
Wait ~600ms Wait while BUSY pin is HIGH

Custom LUT Guide

What is a LUT?

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.

A LUT controls:

  • Voltage level per phase (VSH1, VSH2, VSL, HiZ)
  • VCOM toggling pattern
  • Duration of each phase (TP0TP7)
  • Phase repetitions
  • Additional red-pixel handling

LUT Structure (111 bytes)

Used in the driver implementation for grayscale support:

| Byte Range | Size | Purpose | |------------|------|---------|| | 049 | 50 | VS waveforms (5 groups × 10 bytes) | | 5099 | 50 | TP/RP timing groups (10 groups × 5 bytes) | | 100104 | 5 | Frame rate control | | 105 | 1 | VGH (Gate voltage) - sent via 0x03 | | 106 | 1 | VSH1 (Source voltage 1) - sent via 0x04 | | 107 | 1 | VSH2 (Source voltage 2) - sent via 0x04 | | 108 | 1 | VSL (Source voltage low) - sent via 0x04 | | 109 | 1 | VCOM voltage - sent via 0x2C | | 110 | 1 | Reserved |

Note: Bytes 105-109 are sent using separate voltage control commands after loading the main LUT.

How to Build a Custom LUT

Step 1 — Define Source Voltage Waveform (WS0WS7)

You choose for each phase:

  • VSH1 (medium positive)
  • VSH2 (strong positive — drives white)
  • VSL (strong negative — drives black)
  • HiZ (float)

These define pixel movement direction and strength.

Step 2 — Define VCOM Waveform (WS8WS14)

VCOM biases the entire display.
These bytes define:

  • On/off toggling per phase
  • Matching with source driver phases
  • Ghost reduction

Step 3 — Phase Timing TP0TP7 (WS15WS23)

Each TPx sets duration of a phase. Longer = cleaner image, slower refresh.
Shorter = faster, but potential ghosting.

Step 4 — Repeat Counts & Finalization (WS24WS33)

These adjust:

  • How many times each phase repeats
  • Red pigment extra driving
  • Cleanup phases

How to Load a Custom LUT

A custom LUT is written using Command 0x32:

CMD 0x32
DATA WS0
DATA WS1
...
DATA WS33

The first 105 bytes are written to the LUT register (0x32), followed by separate voltage control commands.

// Load LUT (111-byte format with voltage controls)
void ssd1677_load_lut_extended(const uint8_t* lut) {
    // Load main LUT (105 bytes: VS + TP/RP + frame rate)
    epd_cmd(0x32);
    for (int i = 0; i < 105; i++)
        epd_data(lut[i]);
    
    // Set voltage values from bytes 105-109
    epd_cmd(0x03);  // Gate voltage (VGH)
    epd_data(lut[105]);
    
    epd_cmd(0x04);  // Source voltages (VSH1, VSH2, VSL)
    epd_data(lut[106]);  // VSH1
    epd_data(lut[107]);  // VSH2
    epd_data(lut[108]);  // VSL
    
    epd_cmd(0x2C);  // VCOM voltage
    epd_data(lut[109]);
}

How to Apply (Use) the Custom LUT

After loading the LUT, tell the display to use it.

1. Configure Display Update Mode (0x22)

Typical value enabling LUT usage:

CMD 0x22
DATA 0xF7

2. Start Master Activation (0x20)

CMD 0x20
WAIT BUSY = LOW

While BUSY is high, the LUT waveform is driving the display.

// Apply LUT
void ssd1677_apply_lut() {
    epd_cmd(0x22);
    epd_data(0xF7);   // Use LUT
    epd_cmd(0x20);    // Master Activation
    epd_wait_busy();
}

LUT Summary

Build a custom LUT

  • Create 111 bytes: 105 for LUT register + 5 voltage values + 1 reserved

Use a custom LUT

  1. Write with 0x32
  2. Enable with 0x22
  3. Trigger with 0x20

Optional

  • Burn to OTP with 0x36

Grayscale Rendering with Custom LUTs

The driver implements 4-level grayscale using a multi-pass technique with custom LUTs.

Grayscale Principle

  1. First pass (Black/White): Write BW framebuffer to both RAM buffers, perform standard refresh
  2. Second pass (Grayscale): Write LSB and MSB grayscale buffers, apply custom grayscale LUT, perform fast refresh
  3. The custom LUT creates intermediate gray levels by controlling pixel voltage phases

Grayscale LUT Structure

The driver includes two grayscale LUTs (111 bytes each):

  • lut_grayscale: Forward grayscale rendering
  • lut_grayscale_revert: Cleans up grayscale artifacts back to pure BW

Key characteristics:

  • Uses different voltage sequences for 4 gray levels (00, 01, 10, 11)
  • Frame timing optimized for fast refresh (~500ms)
  • VS waveforms: 50 bytes (5 groups × 10 bytes)
  • TP/RP timing: 50 bytes (10 groups × 5 bytes)
  • Voltages: VGH=0x17, VSH1=0x41, VSH2=0xA8, VSL=0x32, VCOM=0x30

Complete Workflows

Full Screen Update (Initial or Complete Refresh)

1. _InitDisplay() [if not initialized]
2. _setPartialRamArea(0, 0, 800, 480)
3. Write to previous buffer: Command 0x26 + 48000 bytes
4. Write to current buffer: Command 0x24 + 48000 bytes
5. _Update_Full()

Partial Update (Fast Refresh)

1. _InitDisplay() [if not initialized]
2. [First time only] Clear screen buffers with full refresh
3. _setPartialRamArea(x, y, w, h)
4. Write image: Command 0x24 + image bytes
5. _Update_Part()
6. Write image again: Command 0x24 + image bytes
7. Write to previous: Command 0x26 + same image bytes

Why write twice? Partial updates compare current vs previous buffer. Writing to both buffers after refresh prevents ghosting on next update.

Minimal Usage Example

ssd1677_init();

ssd1677_display_frame(bw_image, red_image);

Complete Example: Fast Refresh with Buffering

The driver supports two buffering modes for fast partial updates:

Dual Buffer Mode (Default)

Memory usage: 96KB (two 48KB buffers)

// Initialize display
display.begin();
display.clearScreen(0xFF);
display.displayBuffer(FULL_REFRESH);

// Draw content to framebuffer
uint8_t* fb = display.getFrameBuffer();
// ... draw into fb ...

// Fast refresh (compares with previous frame)
display.displayBuffer(FAST_REFRESH);

// Next frame
// ... modify fb ...
display.displayBuffer(FAST_REFRESH);

How it works:

  1. Two internal buffers (frameBuffer0 and frameBuffer1) alternate as current/previous
  2. On displayBuffer(), current buffer written to BW RAM (0x24), previous to RED RAM (0x26)
  3. Controller compares buffers and only updates changed pixels
  4. Buffers swap roles after each display using swapBuffers()

Single Buffer Mode (Memory Optimized)

Memory usage: 48KB (one 48KB buffer) - saves 48KB RAM

Enable by defining EINK_DISPLAY_SINGLE_BUFFER_MODE before including EInkDisplay.h.

// Initialize display (same as dual buffer)
display.begin();
display.clearScreen(0xFF);
display.displayBuffer(FULL_REFRESH);

// Draw content to framebuffer
uint8_t* fb = display.getFrameBuffer();
// ... draw into fb ...

// Fast refresh (compares with previous frame in display's RED RAM)
display.displayBuffer(FAST_REFRESH);

How it works:

  1. Only one internal buffer (frameBuffer0)
  2. On displayBuffer():
    • FAST_REFRESH: Write new frame to BW RAM (0x24), RED RAM already contains previous frame from last refresh
    • HALF/FULL_REFRESH: Write new frame to both BW and RED RAM (0x24 and 0x26)
    • After refresh, always sync RED RAM with current frame for next differential update
  3. Controller compares BW RAM (new) vs RED RAM (old) for differential updates
  4. RED RAM acts as the "previous frame buffer" for fast refresh

Trade-offs:

  • Pro: Saves 48KB of RAM (critical for ESP32-C3 with only ~380KB usable)
  • Con: Extra RED RAM write after each refresh (~100ms overhead)
  • Con: Cannot preserve screen content during grayscale rendering without external buffer

Grayscale Rendering in Single Buffer Mode

Single buffer mode requires special handling for grayscale rendering since the BW framebuffer is overwritten:

// Store BW buffer after the BW render but before grayscale render
const auto bwBuffer = static_cast<uint8_t *>(malloc(EInkDisplay::BUFFER_SIZE));
const auto frameBuffer = display.getFrameBuffer();
memcpy(bwBuffer, frameBuffer, EInkDisplay::BUFFER_SIZE);

// Perform grayscale rendering (overwrites BW and RED RAM)
display.clearScreen(0x00);
// ... render grayscale LSB to frameBuffer ...
display.copyGrayscaleMsbBuffers(frameBuffer);

display.clearScreen(0x00);
// ... render grayscale MSB to frameBuffer ...
display.copyGrayscaleLsbBuffers(frameBuffer);

// Display grays
display.displayGrayBuffer();

// After grayscale render
display.cleanupGrayscaleBuffers(bwBuffer);
free(bwBuffer);

The cleanupGrayscaleBuffers() method restores the BW buffer to both the framebuffer and RED RAM, ensuring proper state for subsequent fast refreshes

Auto-Write Commands for Fast Clear

Commands 0x46 and 0x47 allow rapid buffer clearing:

// Clear BW RAM to white pattern
sendCommand(0x46);  // Auto write BW RAM
sendData(0xF7);     // Fill pattern
waitWhileBusy();

// Clear RED RAM to white pattern
sendCommand(0x47);  // Auto write RED RAM
sendData(0xF7);     // Fill pattern
waitWhileBusy();

This is much faster than writing 48,000 bytes manually during initialization.


Command Reference

Command Name Purpose
0x01 Driver Output Control Set gate scanning (HEIGHT)
0x0C Booster Soft Start Configure boost converter
0x10 Deep Sleep Mode Enter low power mode
0x11 Data Entry Mode Set X/Y increment direction
0x12 Software Reset Reset controller
0x18 Temperature Sensor Control temp sensor
0x1A Temperature Register Set temp value (fast mode)
0x20 Master Activation Trigger display update
0x21 Display Update Control Configure update mode
0x22 Display Update Sequence Set update waveform
0x24 Write RAM (BW) Write to current buffer
0x26 Write RAM (RED/OLD) Write to previous buffer
0x03 Gate Voltage Set VGH voltage level
0x04 Source Voltage Set VSH1, VSH2, VSL voltages
0x2C Write VCOM Set VCOM voltage
0x32 Write LUT Register Load custom 105-byte LUT (part of 111-byte structure)
0x36 Write OTP Burn LUT to one-time-programmable memory
0x3C Border Waveform Configure border behavior
0x44 Set RAM X Address Define X window (in pixels)
0x45 Set RAM Y Address Define Y window (in pixels)
0x46 Auto Write BW RAM Fast fill BW RAM with pattern
0x47 Auto Write RED RAM Fast fill RED RAM with pattern
0x4E Set RAM X Counter Set initial X position (in pixels)
0x4F Set RAM Y Counter Set initial Y position (in pixels)

Timing & Power Management

Timing Specifications

Operation Duration Notes
Reset pulse 10ms Low duration
Power on ~100ms BUSY signal duration
Power off ~200ms BUSY signal duration
Full refresh ~1600ms Normal mode, wait for BUSY
Partial refresh ~600ms Wait for BUSY
Software reset delay 10ms After command 0x12

BUSY Signal Monitoring

  • Pin: GPIO6 (INPUT)
  • Active level: HIGH
  • Polling: Read pin until LOW, with timeout protection
  • Timeout: 10000ms (10 seconds)
  • Usage: Wait after commands 0x20 (master activation)

Power Off

Command Data Description
0x22 0x83 Power off sequence
0x20 - Master activation
Wait ~200ms Wait while BUSY pin is HIGH

Hibernate (Deep Sleep)

  1. Execute Power Off sequence
  2. Send command 0x10 (Deep Sleep Mode)
  3. Send data 0x01 (Enter deep sleep)

Wake from Hibernate: Requires hardware reset via RST pin.


Important Notes

  • BUSY pin must be polled after reset and update
  • All RAM writes auto-increment based on data entry mode
  • SSD1677 can display BW-only or RED-only if desired
  • All X coordinates and widths must be multiples of 8 (byte boundaries)
  • Y coordinates are reversed in hardware (gates bottom-to-top)
  • RAM auto-increments after each byte transfer
  • Display controller internal RAM: 96,000 bytes (800×480 ÷ 8 * 2 buffers: BW + RED)
  • ESP32 buffer memory usage:
    • Dual buffer mode: 96KB (two 48KB buffers for fast buffer swapping)
    • Single buffer mode: 48KB (one 48KB buffer, uses display's RED RAM for differential)
  • Differential partial updates require RED RAM to contain the previous frame
  • First write after init should be full refresh to clear ghost images
  • Single buffer mode adds ~100ms overhead per refresh (extra RED RAM sync)
  • Grayscale rendering in single buffer mode requires temporary 48KB allocation