Add support for quad color epaper

highlight_color2 is now available for pixel value 0b11.
Black is 0b00, white is 0b01 and highlight_color is 0b10.

Also add support for multibit values written with one command when
color_command isn't provided.
This commit is contained in:
Scott Shawcroft 2025-07-23 15:50:53 -07:00
parent 5202246182
commit 2e183f4c00
No known key found for this signature in database
7 changed files with 56 additions and 19 deletions

View file

@ -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,
@ -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
@ -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 };
@ -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} },
@ -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);
}
@ -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

View file

@ -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,

View file

@ -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) {
@ -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;
@ -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);

View file

@ -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);

View file

@ -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;

View file

@ -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) {

View file

@ -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) {
@ -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;
@ -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,
@ -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
}