Skip to content

Add support for quad color epaper #10519

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
1 change: 1 addition & 0 deletions ports/atmel-samd/boards/openbook_m4/board.c
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ void board_init(void) {
NO_COMMAND, // write_color_ram_command (can add this for grayscale eventually)
false, // color_bits_inverted
0x000000, // highlight_color
0x000000, // highlight_color2
refresh_sequence, // refresh_display_sequence
sizeof(refresh_sequence),
40, // refresh_time
Expand Down
2 changes: 2 additions & 0 deletions ports/espressif/boards/adafruit_magtag_2.9_grayscale/board.c
Original file line number Diff line number Diff line change
Expand Up @@ -239,6 +239,7 @@ void board_init(void) {
0x26, // write_color_ram_command
false, // color_bits_inverted
0x000000, // highlight_color
0x000000, // highlight_color2
ssd1680_display_refresh_sequence, sizeof(ssd1680_display_refresh_sequence),
1.0, // refresh_time
&pin_GPIO5, // busy_pin
Expand Down Expand Up @@ -273,6 +274,7 @@ void board_init(void) {
0x13, // write_color_ram_command
false, // color_bits_inverted
0x000000, // highlight_color
0x000000, // highlight_color2
il0373_display_refresh_sequence, sizeof(il0373_display_refresh_sequence),
1.0, // refresh_time
&pin_GPIO5, // busy_pin
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ void board_init(void) {
0x26, // write_color_ram_command
false, // color_bits_inverted
0x000000, // highlight_color
0x000000, // highlight_color2
refresh_sequence, sizeof(refresh_sequence), // refresh_display_command
1.0, // refresh_time
&pin_GPIO48, // busy_pin
Expand Down
1 change: 1 addition & 0 deletions ports/espressif/boards/heltec_vision_master_e290/board.c
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ void board_init(void) {
0x26, // write_color_ram_command
false, // color_bits_inverted
0xFF0000, // highlight_color
0x000000, // highlight_color2
refresh_sequence, sizeof(refresh_sequence), // refresh_display_command
1.0, // refresh_time
&pin_GPIO6, // busy_pin
Expand Down
1 change: 1 addition & 0 deletions ports/espressif/boards/heltec_wireless_paper/board.c
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,7 @@ void board_init(void) {
0x10, // write_color_ram_command
false, // color_bits_inverted
0x000000, // highlight_color
0x000000, // highlight_color2
refresh_sequence, sizeof(refresh_sequence), // refresh_display_command
1.0, // refresh_time
&pin_GPIO7, // busy_pin
Expand Down
1 change: 1 addition & 0 deletions ports/espressif/boards/sqfmi_watchy/board.c
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,7 @@ void board_init(void) {
0x26, // write_color_ram_command
false, // color_bits_inverted
0x000000, // highlight_color
0x000000, // highlight_color2
refresh_sequence, sizeof(refresh_sequence),
(double)1.0, // refresh_time
&pin_GPIO19, // busy_pin
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -273,6 +273,7 @@ void board_init(void) {
SSD_WRITE_RAM_RED, // write_color_ram_command
false, // color_bits_inverted
0xFF0000, // highlight_color (RED for tri-color display)
0x000000, // highlight_color2
_refresh_sequence_ssd1681, sizeof(_refresh_sequence_ssd1681), // refresh_display_command
refresh_time, // refresh_time
&pin_GPIO9, // DEFAULT_SPI_BUS_BUSY, // busy_pin
Expand Down Expand Up @@ -307,6 +308,7 @@ void board_init(void) {
NO_COMMAND, // write_color_ram_command
false, // color_bits_inverted
0x000000, // highlight_color (RED for tri-color display)
0x000000, // highlight_color2
_refresh_sequence_ssd1608, sizeof(_refresh_sequence_ssd1608), // refresh_display_command
refresh_time, // refresh_time
&pin_GPIO9, // DEFAULT_SPI_BUS_BUSY, // busy_pin
Expand Down
1 change: 1 addition & 0 deletions ports/raspberrypi/boards/pimoroni_badger2040/board.c
Original file line number Diff line number Diff line change
Expand Up @@ -295,6 +295,7 @@ void board_init(void) {
DTM1, // write_color_ram_command
false, // color_bits_inverted
0x000000, // highlight_color
0x000000, // highlight_color2
refresh_sequence, sizeof(refresh_sequence), // refresh_display_command
1.0, // refresh_time
&pin_GPIO26, // busy_pin
Expand Down
1 change: 1 addition & 0 deletions ports/raspberrypi/boards/pimoroni_badger2040w/board.c
Original file line number Diff line number Diff line change
Expand Up @@ -295,6 +295,7 @@ void board_init(void) {
DTM1, // write_color_ram_command
false, // color_bits_inverted
0x000000, // highlight_color
0x000000, // highlight_color2
refresh_sequence, sizeof(refresh_sequence), // refresh_display_command
1.0, // refresh_time
&pin_GPIO26, // busy_pin
Expand Down
1 change: 1 addition & 0 deletions ports/raspberrypi/boards/pimoroni_inky_frame_5_7/board.c
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ void board_init(void) {
NO_COMMAND, // write_color_ram_command
false, // color_bits_inverted
0x000000, // highlight_color
0x000000, // highlight_color2
refresh_sequence, sizeof(refresh_sequence),
28.0, // refresh_time
NULL, // busy_pin
Expand Down
1 change: 1 addition & 0 deletions ports/raspberrypi/boards/pimoroni_inky_frame_7_3/board.c
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,7 @@ void board_init(void) {
NO_COMMAND, // write_color_ram_command
false, // color_bits_inverted
0x000000, // highlight_color
0x000000, // highlight_color2
refresh_sequence, sizeof(refresh_sequence),
28.0, // refresh_time
NULL, // busy_pin
Expand Down
8 changes: 6 additions & 2 deletions shared-bindings/epaperdisplay/EPaperDisplay.c
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@
//| write_color_ram_command: Optional[int] = None,
//| color_bits_inverted: bool = False,
//| highlight_color: int = 0x000000,
//| highlight_color2: int = 0x000000,
//| refresh_display_command: Union[int, circuitpython_typing.ReadableBuffer],
//| refresh_time: float = 40,
//| busy_pin: Optional[microcontroller.Pin] = None,
Expand Down Expand Up @@ -97,6 +98,7 @@
//| :param int write_color_ram_command: Command used to write pixels values into the update region
//| :param bool color_bits_inverted: True if 0 bits are used to show the color. Otherwise, 1 means to show color.
//| :param int highlight_color: RGB888 of source color to highlight with third ePaper color.
//| :param int highlight_color2: RGB888 of source color to highlight with fourth ePaper color.
//| :param int refresh_display_command: Command used to start a display refresh. Single int or byte-packed command sequence
//| :param float refresh_time: Time it takes to refresh the display before the stop_sequence should be sent. Ignored when busy_pin is provided.
//| :param microcontroller.Pin busy_pin: Pin used to signify the display is busy
Expand All @@ -117,7 +119,7 @@ static mp_obj_t epaperdisplay_epaperdisplay_make_new(const mp_obj_type_t *type,
ARG_ram_width, ARG_ram_height, ARG_colstart, ARG_rowstart, ARG_rotation,
ARG_set_column_window_command, ARG_set_row_window_command, ARG_set_current_column_command,
ARG_set_current_row_command, ARG_write_black_ram_command, ARG_black_bits_inverted,
ARG_write_color_ram_command, ARG_color_bits_inverted, ARG_highlight_color,
ARG_write_color_ram_command, ARG_color_bits_inverted, ARG_highlight_color, ARG_highlight_color2,
ARG_refresh_display_command, ARG_refresh_time, ARG_busy_pin, ARG_busy_state,
ARG_seconds_per_frame, ARG_always_toggle_chip_select, ARG_grayscale, ARG_advanced_color_epaper, ARG_spectra6,
ARG_two_byte_sequence_length, ARG_start_up_time, ARG_address_little_endian };
Expand All @@ -141,6 +143,7 @@ static mp_obj_t epaperdisplay_epaperdisplay_make_new(const mp_obj_type_t *type,
{ MP_QSTR_write_color_ram_command, MP_ARG_OBJ | MP_ARG_KW_ONLY, {.u_obj = mp_const_none} },
{ MP_QSTR_color_bits_inverted, MP_ARG_BOOL | MP_ARG_KW_ONLY, {.u_bool = false} },
{ MP_QSTR_highlight_color, MP_ARG_INT | MP_ARG_KW_ONLY, {.u_int = 0x000000} },
{ MP_QSTR_highlight_color2, MP_ARG_INT | MP_ARG_KW_ONLY, {.u_int = 0x000000} },
{ MP_QSTR_refresh_display_command, MP_ARG_OBJ | MP_ARG_REQUIRED },
{ MP_QSTR_refresh_time, MP_ARG_OBJ | MP_ARG_KW_ONLY, {.u_obj = MP_OBJ_NEW_SMALL_INT(40)} },
{ MP_QSTR_busy_pin, MP_ARG_OBJ | MP_ARG_KW_ONLY, {.u_obj = mp_const_none} },
Expand Down Expand Up @@ -181,6 +184,7 @@ static mp_obj_t epaperdisplay_epaperdisplay_make_new(const mp_obj_type_t *type,

mp_int_t write_color_ram_command = NO_COMMAND;
mp_int_t highlight_color = args[ARG_highlight_color].u_int;
mp_int_t highlight_color2 = args[ARG_highlight_color2].u_int;
if (args[ARG_write_color_ram_command].u_obj != mp_const_none) {
write_color_ram_command = mp_obj_get_int(args[ARG_write_color_ram_command].u_obj);
}
Expand Down Expand Up @@ -216,7 +220,7 @@ static mp_obj_t epaperdisplay_epaperdisplay_make_new(const mp_obj_type_t *type,
args[ARG_set_column_window_command].u_int, args[ARG_set_row_window_command].u_int,
args[ARG_set_current_column_command].u_int, args[ARG_set_current_row_command].u_int,
args[ARG_write_black_ram_command].u_int, args[ARG_black_bits_inverted].u_bool, write_color_ram_command,
args[ARG_color_bits_inverted].u_bool, highlight_color, refresh_buf, refresh_buf_len, refresh_time,
args[ARG_color_bits_inverted].u_bool, highlight_color, highlight_color2, refresh_buf, refresh_buf_len, refresh_time,
busy_pin, args[ARG_busy_state].u_bool, seconds_per_frame,
args[ARG_always_toggle_chip_select].u_bool, args[ARG_grayscale].u_bool, args[ARG_advanced_color_epaper].u_bool, args[ARG_spectra6].u_bool,
two_byte_sequence_length, args[ARG_address_little_endian].u_bool
Expand Down
2 changes: 1 addition & 1 deletion shared-bindings/epaperdisplay/EPaperDisplay.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ void common_hal_epaperdisplay_epaperdisplay_construct(epaperdisplay_epaperdispla
uint16_t set_column_window_command, uint16_t set_row_window_command,
uint16_t set_current_column_command, uint16_t set_current_row_command,
uint16_t write_black_ram_command, bool black_bits_inverted,
uint16_t write_color_ram_command, bool color_bits_inverted, uint32_t highlight_color,
uint16_t write_color_ram_command, bool color_bits_inverted, uint32_t highlight_color, uint32_t highlight_color2,
const uint8_t *refresh_sequence, uint16_t refresh_sequence_len, mp_float_t refresh_time,
const mcu_pin_obj_t *busy_pin, bool busy_state, mp_float_t seconds_per_frame,
bool always_toggle_chip_select, bool grayscale, bool acep, bool spectra6, bool two_byte_sequence_length,
Expand Down
41 changes: 30 additions & 11 deletions shared-module/displayio/ColorConverter.c
Original file line number Diff line number Diff line change
Expand Up @@ -163,8 +163,13 @@ uint8_t displayio_colorconverter_compute_sevencolor(uint32_t color_rgb888) {
}
}

void displayio_colorconverter_compute_tricolor(const _displayio_colorspace_t *colorspace, uint8_t pixel_hue, uint32_t *color) {

void displayio_colorconverter_compute_tricolor(const _displayio_colorspace_t *colorspace, uint8_t pixel_chroma, uint8_t pixel_hue, uint32_t *color) {
if (pixel_chroma <= 16) {
if (!colorspace->grayscale) {
*color = 0;
}
return;
}
int16_t hue_diff = colorspace->tricolor_hue - pixel_hue;
if ((-10 <= hue_diff && hue_diff <= 10) || hue_diff <= -220 || hue_diff >= 220) {
if (colorspace->grayscale) {
Expand All @@ -177,6 +182,21 @@ void displayio_colorconverter_compute_tricolor(const _displayio_colorspace_t *co
}
}

void displayio_colorconverter_compute_fourcolor(const _displayio_colorspace_t *colorspace, uint8_t pixel_chroma, uint8_t pixel_hue, uint32_t *color) {
*color >>= 1;
if (pixel_chroma <= 16) {
return;
}
int16_t hue_diff = colorspace->tricolor_hue - pixel_hue;
if ((-10 <= hue_diff && hue_diff <= 10) || hue_diff <= -220 || hue_diff >= 220) {
*color = 2;
}
int16_t hue_diff2 = colorspace->fourcolor_hue - pixel_hue;
if ((-10 <= hue_diff2 && hue_diff2 <= 10) || hue_diff2 <= -220 || hue_diff2 >= 220) {
*color = 3;
}
}

void common_hal_displayio_colorconverter_convert(displayio_colorconverter_t *self, const _displayio_colorspace_t *colorspace, uint32_t input_color, uint32_t *output_color) {
displayio_input_pixel_t input_pixel;
input_pixel.pixel = input_color;
Expand Down Expand Up @@ -313,18 +333,17 @@ void displayio_convert_color(const _displayio_colorspace_t *colorspace, bool dit
output_color->pixel = packed;
output_color->opaque = true;
return;
} else if (colorspace->tricolor) {
} else if (colorspace->tricolor || colorspace->fourcolor) {
uint8_t luma = displayio_colorconverter_compute_luma(pixel);
uint8_t pixel_chroma = displayio_colorconverter_compute_chroma(pixel);
output_color->pixel = luma >> (8 - colorspace->depth);
if (displayio_colorconverter_compute_chroma(pixel) <= 16) {
if (!colorspace->grayscale) {
output_color->pixel = 0;
}
output_color->opaque = true;
return;
}
uint8_t pixel_hue = displayio_colorconverter_compute_hue(pixel);
displayio_colorconverter_compute_tricolor(colorspace, pixel_hue, &output_color->pixel);
if (colorspace->tricolor) {
displayio_colorconverter_compute_tricolor(colorspace, pixel_chroma, pixel_hue, &output_color->pixel);
} else if (colorspace->fourcolor) {
displayio_colorconverter_compute_fourcolor(colorspace, pixel_chroma, pixel_hue, &output_color->pixel);
}
output_color->opaque = true;
return;
} else if (colorspace->grayscale && colorspace->depth <= 8) {
uint8_t luma = displayio_colorconverter_compute_luma(pixel);
Expand Down
3 changes: 2 additions & 1 deletion shared-module/displayio/ColorConverter.h
Original file line number Diff line number Diff line change
Expand Up @@ -43,4 +43,5 @@ uint8_t displayio_colorconverter_compute_chroma(uint32_t color_rgb888);
uint8_t displayio_colorconverter_compute_hue(uint32_t color_rgb888);
uint8_t displayio_colorconverter_compute_sixcolor(uint32_t color_rgb888);
uint8_t displayio_colorconverter_compute_sevencolor(uint32_t color_rgb888);
void displayio_colorconverter_compute_tricolor(const _displayio_colorspace_t *colorspace, uint8_t pixel_hue, uint32_t *color);
void displayio_colorconverter_compute_tricolor(const _displayio_colorspace_t *colorspace, uint8_t pixel_chroma, uint8_t pixel_hue, uint32_t *color);
void displayio_colorconverter_compute_fourcolor(const _displayio_colorspace_t *colorspace, uint8_t pixel_chroma, uint8_t pixel_hue, uint32_t *color);
3 changes: 2 additions & 1 deletion shared-module/displayio/Palette.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,11 @@ typedef struct {
uint8_t depth;
uint8_t bytes_per_cell;
uint8_t tricolor_hue;
uint8_t tricolor_luma;
uint8_t fourcolor_hue;
uint8_t grayscale_bit; // The lowest grayscale bit. Normally 8 - depth.
bool grayscale;
bool tricolor;
bool fourcolor;
bool sixcolor; // Spectra6 e-ink screens.
bool sevencolor; // Acep e-ink screens.
bool pixels_in_byte_share_row;
Expand Down
1 change: 1 addition & 0 deletions shared-module/displayio/__init__.c
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,7 @@ void reset_displays(void) {
for (uint8_t i = 0; i < CIRCUITPY_DISPLAY_LIMIT; i++) {
mp_const_obj_t display_bus_type = display_buses[i].bus_base.type;
if (display_bus_type == NULL || display_bus_type == &mp_type_NoneType) {
display_buses[i].bus_base.type = &mp_type_NoneType;
continue;
#if CIRCUITPY_FOURWIRE
} else if (display_bus_type == &fourwire_fourwire_type) {
Expand Down
17 changes: 14 additions & 3 deletions shared-module/epaperdisplay/EPaperDisplay.c
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ void common_hal_epaperdisplay_epaperdisplay_construct(epaperdisplay_epaperdispla
uint16_t set_column_window_command, uint16_t set_row_window_command,
uint16_t set_current_column_command, uint16_t set_current_row_command,
uint16_t write_black_ram_command, bool black_bits_inverted,
uint16_t write_color_ram_command, bool color_bits_inverted, uint32_t highlight_color,
uint16_t write_color_ram_command, bool color_bits_inverted, uint32_t highlight_color, uint32_t highlight_color2,
const uint8_t *refresh_sequence, uint16_t refresh_sequence_len, mp_float_t refresh_time,
const mcu_pin_obj_t *busy_pin, bool busy_state, mp_float_t seconds_per_frame,
bool chip_select, bool grayscale, bool acep, bool spectra6, bool two_byte_sequence_length, bool address_little_endian) {
Expand All @@ -42,10 +42,16 @@ void common_hal_epaperdisplay_epaperdisplay_construct(epaperdisplay_epaperdispla
if (highlight_color != 0x000000) {
self->core.colorspace.tricolor = true;
self->core.colorspace.tricolor_hue = displayio_colorconverter_compute_hue(highlight_color);
self->core.colorspace.tricolor_luma = displayio_colorconverter_compute_luma(highlight_color);
} else {
self->core.colorspace.tricolor = false;
}
if (highlight_color != 0x000000 && highlight_color2 != 0x000000) {
self->core.colorspace.tricolor = false;
self->core.colorspace.fourcolor = true;
self->core.colorspace.fourcolor_hue = displayio_colorconverter_compute_hue(highlight_color2);
} else {
self->core.colorspace.fourcolor = false;
}
self->acep = acep || spectra6;
self->core.colorspace.sixcolor = spectra6;
self->core.colorspace.sevencolor = acep;
Expand All @@ -54,6 +60,11 @@ void common_hal_epaperdisplay_epaperdisplay_construct(epaperdisplay_epaperdispla
grayscale = false;
core_grayscale = false;
}
if ((highlight_color != 0x000000 || highlight_color2 != 0x000000) && write_color_ram_command == NO_COMMAND) {
color_depth = 2;
core_grayscale = false;
grayscale = false;
}

displayio_display_core_construct(&self->core, width, height, rotation, color_depth, core_grayscale, true, 1, true, true);
displayio_display_bus_construct(&self->bus, bus, ram_width, ram_height,
Expand Down Expand Up @@ -90,7 +101,7 @@ void common_hal_epaperdisplay_epaperdisplay_construct(epaperdisplay_epaperdispla
}

// Clear the color memory if it isn't in use.
if (highlight_color == 0x00 && write_color_ram_command != NO_COMMAND) {
if (highlight_color == 0x00 && highlight_color2 == 0x00 && write_color_ram_command != NO_COMMAND) {
// TODO: Clear
}

Expand Down