From c16d316649e36b2074411f1168ffec0f63035a76 Mon Sep 17 00:00:00 2001 From: Mikey Sklar Date: Fri, 8 May 2026 15:05:37 -0700 Subject: [PATCH 1/3] MagTag SSD1680 FPC-7519rev.b: use VCOM=0x20 for correct contrast The FPC-7519rev.b panel (User ID byte 0xca) shows a dark gray background with the default VCOM=0x28. Setting VCOM=0x20 (-1.5V) gives a light gray background with solid black text. Surgical fix: dedicated ssd1680_vcom20_display_start_sequence and DISPLAY_SSD1680_COLSTART_8_VCOM20 type routed to case 0xca: only. The 0x44 and 0x00 panels are untouched. --- .../adafruit_magtag_2.9_grayscale/board.c | 49 +++++++++++++++++-- 1 file changed, 45 insertions(+), 4 deletions(-) diff --git a/ports/espressif/boards/adafruit_magtag_2.9_grayscale/board.c b/ports/espressif/boards/adafruit_magtag_2.9_grayscale/board.c index 701714db4b821..e8304dded4bd5 100644 --- a/ports/espressif/boards/adafruit_magtag_2.9_grayscale/board.c +++ b/ports/espressif/boards/adafruit_magtag_2.9_grayscale/board.c @@ -128,6 +128,40 @@ const uint8_t ssd1680_display_start_sequence[] = { 0x22, 0x00, 0x01, 0xc7 // display update mode }; +// FPC-7519rev.b (User ID byte 0xca) requires VCOM=0x20 (-1.5V) for correct contrast. +// The 0x44 panel works correctly with the default VCOM=0x28, so keep them separate. +const uint8_t ssd1680_vcom20_display_start_sequence[] = { + 0x12, DELAY, 0x00, 0x14, // soft reset and wait 20ms + 0x11, 0x00, 0x01, 0x03, // Ram data entry mode + 0x3c, 0x00, 0x01, 0x03, // border color + 0x2c, 0x00, 0x01, 0x20, // Set vcom voltage (0x20 = -1.5V, tuned for FPC-7519rev.b) + 0x03, 0x00, 0x01, 0x17, // Set gate voltage + 0x04, 0x00, 0x03, 0x41, 0xae, 0x32, // Set source voltage + 0x4e, 0x00, 0x01, 0x01, // ram x count + 0x4f, 0x00, 0x02, 0x00, 0x00, // ram y count + 0x01, 0x00, 0x03, 0x27, 0x01, 0x00, // set display size + 0x32, 0x00, 0x99, // Update waveforms + 0x2a, 0x60, 0x15, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // VS L0 + 0x20, 0x60, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // VS L1 + 0x28, 0x60, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // VS L2 + 0x00, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // VS L3 + 0x00, 0x90, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // VS L4 + 0x00, 0x02, 0x00, 0x05, 0x14, 0x00, 0x00, // TP, SR, RP of Group0 + 0x1E, 0x1E, 0x00, 0x00, 0x00, 0x00, 0x01, // TP, SR, RP of Group1 + 0x00, 0x02, 0x00, 0x05, 0x14, 0x00, 0x00, // TP, SR, RP of Group2 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // TP, SR, RP of Group3 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // TP, SR, RP of Group4 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // TP, SR, RP of Group5 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // TP, SR, RP of Group6 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // TP, SR, RP of Group7 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // TP, SR, RP of Group8 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // TP, SR, RP of Group9 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // TP, SR, RP of Group10 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // TP, SR, RP of Group11 + 0x24, 0x22, 0x22, 0x22, 0x23, 0x32, 0x00, 0x00, 0x00, // FR, XON + 0x22, 0x00, 0x01, 0xc7 // display update mode +}; + const uint8_t ssd1680_display_stop_sequence[] = { 0x10, DELAY, 0x01, 0x01, 0x64 }; @@ -141,6 +175,7 @@ typedef enum { DISPLAY_IL0373, DISPLAY_SSD1680_COLSTART_0, DISPLAY_SSD1680_COLSTART_8, + DISPLAY_SSD1680_COLSTART_8_VCOM20, // FPC-7519rev.b (User ID 0xca) } display_type_t; static display_type_t detect_display_type(void) { @@ -218,8 +253,9 @@ static display_type_t detect_display_type(void) { case 0x00: return DISPLAY_SSD1680_COLSTART_0; case 0x44: - case 0xca: return DISPLAY_SSD1680_COLSTART_8; + case 0xca: + return DISPLAY_SSD1680_COLSTART_8_VCOM20; } } @@ -268,12 +304,17 @@ void board_init(void) { } else { epaperdisplay_construct_args_t args = EPAPERDISPLAY_CONSTRUCT_ARGS_DEFAULTS; // Default colstart is 0. - if (display_type == DISPLAY_SSD1680_COLSTART_8) { + if (display_type == DISPLAY_SSD1680_COLSTART_8 || display_type == DISPLAY_SSD1680_COLSTART_8_VCOM20) { args.colstart = 8; } args.bus = bus; - args.start_sequence = ssd1680_display_start_sequence; - args.start_sequence_len = sizeof(ssd1680_display_start_sequence); + if (display_type == DISPLAY_SSD1680_COLSTART_8_VCOM20) { + args.start_sequence = ssd1680_vcom20_display_start_sequence; + args.start_sequence_len = sizeof(ssd1680_vcom20_display_start_sequence); + } else { + args.start_sequence = ssd1680_display_start_sequence; + args.start_sequence_len = sizeof(ssd1680_display_start_sequence); + } args.stop_sequence = ssd1680_display_stop_sequence; args.stop_sequence_len = sizeof(ssd1680_display_stop_sequence); args.width = 296; From 5dd10979e6bf115877635b7c1fe9f03ba2d2c795 Mon Sep 17 00:00:00 2001 From: Mikey Sklar Date: Fri, 8 May 2026 17:18:52 -0700 Subject: [PATCH 2/3] MagTag SSD1680 FPC-7519rev.b: tune VCOM to 0x14 (-1.0V) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Testing across 0x28→0x20→0x1c→0x18→0x14→0x10 confirmed 0x14 as the sweet spot: background matches the FPC-7619rev.b panel without the dark halo fringe artifacts that appear below 0x14. Co-Authored-By: Claude Sonnet 4.6 --- ports/espressif/boards/adafruit_magtag_2.9_grayscale/board.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ports/espressif/boards/adafruit_magtag_2.9_grayscale/board.c b/ports/espressif/boards/adafruit_magtag_2.9_grayscale/board.c index e8304dded4bd5..a988180f14af9 100644 --- a/ports/espressif/boards/adafruit_magtag_2.9_grayscale/board.c +++ b/ports/espressif/boards/adafruit_magtag_2.9_grayscale/board.c @@ -128,13 +128,13 @@ const uint8_t ssd1680_display_start_sequence[] = { 0x22, 0x00, 0x01, 0xc7 // display update mode }; -// FPC-7519rev.b (User ID byte 0xca) requires VCOM=0x20 (-1.5V) for correct contrast. +// FPC-7519rev.b (User ID byte 0xca) requires lower VCOM for correct contrast. // The 0x44 panel works correctly with the default VCOM=0x28, so keep them separate. const uint8_t ssd1680_vcom20_display_start_sequence[] = { 0x12, DELAY, 0x00, 0x14, // soft reset and wait 20ms 0x11, 0x00, 0x01, 0x03, // Ram data entry mode 0x3c, 0x00, 0x01, 0x03, // border color - 0x2c, 0x00, 0x01, 0x20, // Set vcom voltage (0x20 = -1.5V, tuned for FPC-7519rev.b) + 0x2c, 0x00, 0x01, 0x14, // Set vcom voltage (0x14 = -1.0V, tuned for FPC-7519rev.b) 0x03, 0x00, 0x01, 0x17, // Set gate voltage 0x04, 0x00, 0x03, 0x41, 0xae, 0x32, // Set source voltage 0x4e, 0x00, 0x01, 0x01, // ram x count From 83576c6a1063afbf0ee156e08c64809d7303f716 Mon Sep 17 00:00:00 2001 From: Mikey Sklar Date: Fri, 8 May 2026 18:33:57 -0700 Subject: [PATCH 3/3] =?UTF-8?q?MagTag=20SSD1680=20FPC-7519rev.b:=20rename?= =?UTF-8?q?=20vcom20=20=E2=86=92=20vcom14,=20add=20OTP=20note?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Rename ssd1680_vcom20_display_start_sequence → ssd1680_vcom14_display_start_sequence and DISPLAY_SSD1680_COLSTART_8_VCOM20 → DISPLAY_SSD1680_COLSTART_8_VCOM14 to match the actual register value being set (0x14, not the earlier test value 0x20). Add comment noting that VCOM=0x14 was independently confirmed by reading OTP register 0x2D on the physical panel — byte 1 of the response is 0x14, matching what the panel manufacturer programmed as the recommended VCOM for this revision. Co-Authored-By: Claude Sonnet 4.6 --- .../boards/adafruit_magtag_2.9_grayscale/board.c | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/ports/espressif/boards/adafruit_magtag_2.9_grayscale/board.c b/ports/espressif/boards/adafruit_magtag_2.9_grayscale/board.c index a988180f14af9..cd3295a2e087c 100644 --- a/ports/espressif/boards/adafruit_magtag_2.9_grayscale/board.c +++ b/ports/espressif/boards/adafruit_magtag_2.9_grayscale/board.c @@ -129,8 +129,9 @@ const uint8_t ssd1680_display_start_sequence[] = { }; // FPC-7519rev.b (User ID byte 0xca) requires lower VCOM for correct contrast. +// VCOM=0x14 (-1.0V) confirmed by reading the panel's OTP register (cmd 0x2D, byte 1 = 0x14). // The 0x44 panel works correctly with the default VCOM=0x28, so keep them separate. -const uint8_t ssd1680_vcom20_display_start_sequence[] = { +const uint8_t ssd1680_vcom14_display_start_sequence[] = { 0x12, DELAY, 0x00, 0x14, // soft reset and wait 20ms 0x11, 0x00, 0x01, 0x03, // Ram data entry mode 0x3c, 0x00, 0x01, 0x03, // border color @@ -175,7 +176,7 @@ typedef enum { DISPLAY_IL0373, DISPLAY_SSD1680_COLSTART_0, DISPLAY_SSD1680_COLSTART_8, - DISPLAY_SSD1680_COLSTART_8_VCOM20, // FPC-7519rev.b (User ID 0xca) + DISPLAY_SSD1680_COLSTART_8_VCOM14, // FPC-7519rev.b (User ID 0xca) } display_type_t; static display_type_t detect_display_type(void) { @@ -255,7 +256,7 @@ static display_type_t detect_display_type(void) { case 0x44: return DISPLAY_SSD1680_COLSTART_8; case 0xca: - return DISPLAY_SSD1680_COLSTART_8_VCOM20; + return DISPLAY_SSD1680_COLSTART_8_VCOM14; } } @@ -304,13 +305,13 @@ void board_init(void) { } else { epaperdisplay_construct_args_t args = EPAPERDISPLAY_CONSTRUCT_ARGS_DEFAULTS; // Default colstart is 0. - if (display_type == DISPLAY_SSD1680_COLSTART_8 || display_type == DISPLAY_SSD1680_COLSTART_8_VCOM20) { + if (display_type == DISPLAY_SSD1680_COLSTART_8 || display_type == DISPLAY_SSD1680_COLSTART_8_VCOM14) { args.colstart = 8; } args.bus = bus; - if (display_type == DISPLAY_SSD1680_COLSTART_8_VCOM20) { - args.start_sequence = ssd1680_vcom20_display_start_sequence; - args.start_sequence_len = sizeof(ssd1680_vcom20_display_start_sequence); + if (display_type == DISPLAY_SSD1680_COLSTART_8_VCOM14) { + args.start_sequence = ssd1680_vcom14_display_start_sequence; + args.start_sequence_len = sizeof(ssd1680_vcom14_display_start_sequence); } else { args.start_sequence = ssd1680_display_start_sequence; args.start_sequence_len = sizeof(ssd1680_display_start_sequence);