Begin refresh rework.

crypto-aes
Scott Shawcroft 3 years ago
parent d37dd4d758
commit c247e7df9c
No known key found for this signature in database
GPG Key ID: 9349BC7E64B1921E
  1. 2
      ports/atmel-samd/background.c
  2. 2
      ports/nrf/background.c
  3. 2
      ports/stm32f4/background.c
  4. 2
      py/circuitpy_mpconfig.h
  5. 37
      shared-bindings/displayio/Display.c
  6. 14
      shared-bindings/displayio/Display.h
  7. 45
      shared-bindings/displayio/EPaperDisplay.c
  8. 9
      shared-bindings/displayio/EPaperDisplay.h
  9. 124
      shared-module/displayio/Display.c
  10. 8
      shared-module/displayio/Display.h
  11. 71
      shared-module/displayio/EPaperDisplay.c
  12. 8
      shared-module/displayio/EPaperDisplay.h
  13. 149
      shared-module/displayio/__init__.c
  14. 2
      shared-module/displayio/__init__.h

@ -60,7 +60,7 @@ void run_background_tasks(void) {
audio_dma_background();
#endif
#if CIRCUITPY_DISPLAYIO
displayio_refresh_displays();
displayio_background();
#endif
#if CIRCUITPY_NETWORK

@ -56,7 +56,7 @@ void run_background_tasks(void) {
#endif
#if CIRCUITPY_DISPLAYIO
displayio_refresh_displays();
displayio_background();
#endif
running_background_tasks = false;

@ -49,7 +49,7 @@ void run_background_tasks(void) {
//usb_background();
#if CIRCUITPY_DISPLAYIO
displayio_refresh_displays();
displayio_background();
#endif
running_background_tasks = false;

@ -307,7 +307,7 @@ extern const struct _mp_obj_module_t terminalio_module;
#define DISPLAYIO_MODULE { MP_OBJ_NEW_QSTR(MP_QSTR_displayio), (mp_obj_t)&displayio_module },
#define FONTIO_MODULE { MP_OBJ_NEW_QSTR(MP_QSTR_fontio), (mp_obj_t)&fontio_module },
#define TERMINALIO_MODULE { MP_OBJ_NEW_QSTR(MP_QSTR_terminalio), (mp_obj_t)&terminalio_module },
#define CIRCUITPY_DISPLAY_LIMIT (3)
#define CIRCUITPY_DISPLAY_LIMIT (1)
#else
#define DISPLAYIO_MODULE
#define FONTIO_MODULE

@ -212,27 +212,42 @@ STATIC mp_obj_t displayio_display_obj_show(mp_obj_t self_in, mp_obj_t group_in)
}
MP_DEFINE_CONST_FUN_OBJ_2(displayio_display_show_obj, displayio_display_obj_show);
//| .. method:: refresh_soon()
//| .. method:: refresh(*, target_frames_per_second=None, minimum_frames_per_second=1)
//|
//| Queues up a display refresh that happens in the background.
//| Waits for the target frame rate and then refreshes the display. If the call is too late for the given target frame rate, then the refresh returns immediately without updating the screen to hopefully help getting caught up. If the current frame rate is below the minimum frame rate, then an exception will be raised.
//|
STATIC mp_obj_t displayio_display_obj_refresh_soon(mp_obj_t self_in) {
STATIC mp_obj_t displayio_display_obj_refresh(mp_obj_t self_in) {
displayio_display_obj_t *self = native_display(self_in);
common_hal_displayio_display_refresh_soon(self);
common_hal_displayio_display_refresh(self);
return mp_const_none;
}
MP_DEFINE_CONST_FUN_OBJ_1(displayio_display_refresh_soon_obj, displayio_display_obj_refresh_soon);
//| .. method:: wait_for_frame()
//| .. attribute:: auto_refresh
//|
//| Waits until the next frame has been transmitted to the display unless the wait count is
//| behind the rendered frames. In that case, this will return immediately with the wait count.
//| True when the display is refreshed automatically.
//|
STATIC mp_obj_t displayio_display_obj_wait_for_frame(mp_obj_t self_in) {
STATIC mp_obj_t displayio_display_obj_get_auto_refresh(mp_obj_t self_in) {
displayio_display_obj_t *self = native_display(self_in);
return MP_OBJ_NEW_SMALL_INT(common_hal_displayio_display_wait_for_frame(self));
return mp_obj_new_bool(common_hal_displayio_display_get_auto_refresh(self));
}
MP_DEFINE_CONST_FUN_OBJ_1(displayio_display_wait_for_frame_obj, displayio_display_obj_wait_for_frame);
MP_DEFINE_CONST_FUN_OBJ_1(displayio_display_get_auto_refresh_obj, displayio_display_obj_get_auto_refresh);
STATIC mp_obj_t displayio_display_obj_set_auto_refresh(mp_obj_t self_in, mp_obj_t auto_refresh) {
displayio_display_obj_t *self = native_display(self_in);
common_hal_displayio_display_set_auto_refresh(self, mp_obj_is_true(auto_refresh));
return mp_const_none;
}
MP_DEFINE_CONST_FUN_OBJ_2(displayio_display_set_auto_refresh_obj, displayio_display_obj_set_auto_refresh);
const mp_obj_property_t displayio_display_auto_refresh_obj = {
.base.type = &mp_type_property,
.proxy = {(mp_obj_t)&displayio_display_get_auto_refresh_obj,
(mp_obj_t)&displayio_display_set_auto_refresh_obj,
(mp_obj_t)&mp_const_none_obj},
};
//| .. attribute:: brightness
//|
@ -442,6 +457,8 @@ STATIC const mp_rom_map_elem_t displayio_display_locals_dict_table[] = {
{ MP_ROM_QSTR(MP_QSTR_wait_for_frame), MP_ROM_PTR(&displayio_display_wait_for_frame_obj) },
{ MP_ROM_QSTR(MP_QSTR_fill_row), MP_ROM_PTR(&displayio_display_fill_row_obj) },
{ MP_ROM_QSTR(MP_QSTR_auto_refresh), MP_ROM_PTR(&displayio_display_auto_refresh_obj) },
{ MP_ROM_QSTR(MP_QSTR_brightness), MP_ROM_PTR(&displayio_display_brightness_obj) },
{ MP_ROM_QSTR(MP_QSTR_auto_brightness), MP_ROM_PTR(&displayio_display_auto_brightness_obj) },

@ -44,26 +44,16 @@ void common_hal_displayio_display_construct(displayio_display_obj_t* self,
bool pixels_in_byte_share_row, uint8_t bytes_per_cell, bool reverse_pixels_in_byte,
uint8_t set_column_command, uint8_t set_row_command, uint8_t write_ram_command, uint8_t set_vertical_scroll,
uint8_t* init_sequence, uint16_t init_sequence_len, const mcu_pin_obj_t* backlight_pin, uint16_t brightness_command,
mp_float_t brightness, bool auto_brightness,
mp_float_t brightness, bool auto_brightness, bool auto_refresh, uint8_t frames_per_second,
bool single_byte_bounds, bool data_as_commands);
int32_t common_hal_displayio_display_wait_for_frame(displayio_display_obj_t* self);
bool common_hal_displayio_display_show(displayio_display_obj_t* self, displayio_group_t* root_group);
void common_hal_displayio_display_refresh_soon(displayio_display_obj_t* self);
void common_hal_displayio_display_refresh(displayio_display_obj_t* self);
bool displayio_display_begin_transaction(displayio_display_obj_t* self);
void displayio_display_end_transaction(displayio_display_obj_t* self);
// The second point of the region is exclusive.
void displayio_display_set_region_to_update(displayio_display_obj_t* self, displayio_area_t* area);
bool displayio_display_frame_queued(displayio_display_obj_t* self);
bool displayio_display_refresh_queued(displayio_display_obj_t* self);
void displayio_display_finish_refresh(displayio_display_obj_t* self);
void displayio_display_send_pixels(displayio_display_obj_t* self, uint8_t* pixels, uint32_t length);
bool common_hal_displayio_display_get_auto_brightness(displayio_display_obj_t* self);
void common_hal_displayio_display_set_auto_brightness(displayio_display_obj_t* self, bool auto_brightness);

@ -51,7 +51,7 @@
//| Most people should not use this class directly. Use a specific display driver instead that will
//| contain the startup and shutdown sequences at minimum.
//|
//| .. class:: EPaperDisplay(display_bus, start_sequence, stop_sequence, *, width, height, ram_width, ram_height, colstart=0, rowstart=0, rotation=0, set_column_window_command=None, set_row_window_command=None, single_byte_bounds=False, write_black_ram_command, black_bits_inverted=False, write_color_ram_command=None, color_bits_inverted=False, refresh_display_command, busy_pin=None, busy_state=True, seconds_per_frame=180, always_toggle_chip_select=False)
//| .. class:: EPaperDisplay(display_bus, start_sequence, stop_sequence, *, width, height, ram_width, ram_height, colstart=0, rowstart=0, rotation=0, set_column_window_command=None, set_row_window_command=None, single_byte_bounds=False, write_black_ram_command, black_bits_inverted=False, write_color_ram_command=None, color_bits_inverted=False, highlight_color=0x000000, refresh_display_command, busy_pin=None, busy_state=True, seconds_per_frame=180, always_toggle_chip_select=False)
//|
//| Create a EPaperDisplay object on the given display bus (`displayio.FourWire` or `displayio.ParallelBus`).
//|
@ -81,7 +81,7 @@
//| :param bool black_bits_inverted: True if 0 bits are used to show black pixels. Otherwise, 1 means to show black.
//| :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 third_color: Color of third ePaper color in RGB888.
//| :param int highlight_color: RGB888 of source color to highlight with third ePaper color.
//| :param int refresh_display_command: Command used to start a display refresh
//| :param microcontroller.Pin busy_pin: Pin used to signify the display is busy
//| :param bool busy_state: State of the busy pin when the display is busy
@ -89,7 +89,7 @@
//| :param bool always_toggle_chip_select: When True, chip select is toggled every byte
//|
STATIC mp_obj_t displayio_epaperdisplay_make_new(const mp_obj_type_t *type, size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
enum { ARG_display_bus, ARG_start_sequence, ARG_stop_sequence, ARG_width, ARG_height, 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_third_color, ARG_refresh_display_command, ARG_busy_pin, ARG_busy_state, ARG_seconds_per_frame, ARG_always_toggle_chip_select };
enum { ARG_display_bus, ARG_start_sequence, ARG_stop_sequence, ARG_width, ARG_height, 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_refresh_display_command, ARG_busy_pin, ARG_busy_state, ARG_seconds_per_frame, ARG_always_toggle_chip_select };
static const mp_arg_t allowed_args[] = {
{ MP_QSTR_display_bus, MP_ARG_REQUIRED | MP_ARG_OBJ },
{ MP_QSTR_start_sequence, MP_ARG_REQUIRED | MP_ARG_OBJ },
@ -109,7 +109,7 @@ STATIC mp_obj_t displayio_epaperdisplay_make_new(const mp_obj_type_t *type, size
{ MP_QSTR_black_bits_inverted, MP_ARG_BOOL | MP_ARG_KW_ONLY, {.u_bool = false} },
{ 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_third_color, MP_ARG_INT | MP_ARG_KW_ONLY, {.u_int = 0x000000} },
{ MP_QSTR_highlight_color, MP_ARG_INT | MP_ARG_KW_ONLY, {.u_int = 0x000000} },
{ MP_QSTR_refresh_display_command, MP_ARG_INT | MP_ARG_REQUIRED },
{ MP_QSTR_busy_pin, MP_ARG_OBJ | MP_ARG_KW_ONLY, {.u_obj = mp_const_none} },
{ MP_QSTR_busy_state, MP_ARG_BOOL | MP_ARG_KW_ONLY, {.u_bool = true} },
@ -155,7 +155,7 @@ STATIC mp_obj_t displayio_epaperdisplay_make_new(const mp_obj_type_t *type, size
mp_float_t seconds_per_frame = mp_obj_get_float(args[ARG_seconds_per_frame].u_obj);
mp_int_t write_color_ram_command = NO_COMMAND;
mp_int_t third_color = args[ARG_third_color].u_int;
mp_int_t highlight_color = args[ARG_highlight_color].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);
}
@ -168,7 +168,7 @@ STATIC mp_obj_t displayio_epaperdisplay_make_new(const mp_obj_type_t *type, size
args[ARG_width].u_int, args[ARG_height].u_int, args[ARG_ram_width].u_int, args[ARG_ram_height].u_int, args[ARG_colstart].u_int, args[ARG_rowstart].u_int, rotation,
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, third_color, args[ARG_refresh_display_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, args[ARG_refresh_display_command].u_int,
busy_pin, args[ARG_busy_state].u_bool, seconds_per_frame, args[ARG_always_toggle_chip_select].u_bool
);
@ -203,27 +203,38 @@ STATIC mp_obj_t displayio_epaperdisplay_obj_show(mp_obj_t self_in, mp_obj_t grou
}
MP_DEFINE_CONST_FUN_OBJ_2(displayio_epaperdisplay_show_obj, displayio_epaperdisplay_obj_show);
//| .. method:: refresh_soon()
//| .. method:: refresh()
//|
//| Queues up a display refresh that happens in the background.
//| Refreshes the display immediately or raises an exception if too soon. Use
//| ``time.sleep(display.time_to_refresh)`` to sleep until a refresh can occur.
//|
STATIC mp_obj_t displayio_epaperdisplay_obj_refresh_soon(mp_obj_t self_in) {
displayio_epaperdisplay_obj_t *self = native_display(self_in);
common_hal_displayio_epaperdisplay_refresh_soon(self);
bool ok = common_hal_displayio_epaperdisplay_refresh(self);
if (!ok) {
mp_raise_RuntimeError(translate("Refresh too soon"));
}
return mp_const_none;
}
MP_DEFINE_CONST_FUN_OBJ_1(displayio_epaperdisplay_refresh_soon_obj, displayio_epaperdisplay_obj_refresh_soon);
//| .. method:: wait_for_frame()
//| .. attribute:: time_to_refresh
//|
//| Waits until the next frame has been transmitted to the display unless the wait count is
//| behind the rendered frames. In that case, this will return immediately with the wait count.
//| Time, in fractional seconds, until the ePaper display can be refreshed.
//|
STATIC mp_obj_t displayio_epaperdisplay_obj_wait_for_frame(mp_obj_t self_in) {
//|
STATIC mp_obj_t displayio_epaperdisplay_obj_get_time_to_refresh(mp_obj_t self_in) {
displayio_epaperdisplay_obj_t *self = native_display(self_in);
return MP_OBJ_NEW_SMALL_INT(common_hal_displayio_epaperdisplay_wait_for_frame(self));
return mp_obj_new_float(common_hal_displayio_epaperdisplay_get_time_to_refresh(self));
}
MP_DEFINE_CONST_FUN_OBJ_1(displayio_epaperdisplay_wait_for_frame_obj, displayio_epaperdisplay_obj_wait_for_frame);
MP_DEFINE_CONST_FUN_OBJ_1(displayio_epaperdisplay_get_time_to_refresh_obj, displayio_epaperdisplay_obj_get_time_to_refresh);
const mp_obj_property_t displayio_epaperdisplay_time_to_refresh_obj = {
.base.type = &mp_type_property,
.proxy = {(mp_obj_t)&displayio_epaperdisplay_get_time_to_refresh_obj,
(mp_obj_t)&mp_const_none_obj,
(mp_obj_t)&mp_const_none_obj},
};
//| .. attribute:: width
//|
@ -282,12 +293,12 @@ const mp_obj_property_t displayio_epaperdisplay_bus_obj = {
STATIC const mp_rom_map_elem_t displayio_epaperdisplay_locals_dict_table[] = {
{ MP_ROM_QSTR(MP_QSTR_show), MP_ROM_PTR(&displayio_epaperdisplay_show_obj) },
{ MP_ROM_QSTR(MP_QSTR_refresh_soon), MP_ROM_PTR(&displayio_epaperdisplay_refresh_soon_obj) },
{ MP_ROM_QSTR(MP_QSTR_wait_for_frame), MP_ROM_PTR(&displayio_epaperdisplay_wait_for_frame_obj) },
{ MP_ROM_QSTR(MP_QSTR_refresh_soon), MP_ROM_PTR(&displayio_epaperdisplay_refresh_obj) },
{ MP_ROM_QSTR(MP_QSTR_width), MP_ROM_PTR(&displayio_epaperdisplay_width_obj) },
{ MP_ROM_QSTR(MP_QSTR_height), MP_ROM_PTR(&displayio_epaperdisplay_height_obj) },
{ MP_ROM_QSTR(MP_QSTR_bus), MP_ROM_PTR(&displayio_epaperdisplay_bus_obj) },
{ MP_ROM_QSTR(MP_QSTR_time_to_refresh), MP_ROM_PTR(&displayio_epaperdisplay_time_to_refresh_obj) },
};
STATIC MP_DEFINE_CONST_DICT(displayio_epaperdisplay_locals_dict, displayio_epaperdisplay_locals_dict_table);

@ -43,23 +43,24 @@ void common_hal_displayio_epaperdisplay_construct(displayio_epaperdisplay_obj_t*
uint16_t width, uint16_t height, uint16_t ram_width, uint16_t ram_height, int16_t colstart, int16_t rowstart, uint16_t rotation,
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 third_color, uint16_t refresh_display_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 refresh_display_command,
const mcu_pin_obj_t* busy_pin, bool busy_state, mp_float_t seconds_per_frame, bool always_toggle_chip_select);
int32_t common_hal_displayio_epaperdisplay_wait_for_frame(displayio_epaperdisplay_obj_t* self);
bool common_hal_displayio_epaperdisplay_show(displayio_epaperdisplay_obj_t* self, displayio_group_t* root_group);
void common_hal_displayio_epaperdisplay_refresh_soon(displayio_epaperdisplay_obj_t* self);
bool displayio_epaperdisplay_begin_transaction(displayio_epaperdisplay_obj_t* self);
void displayio_epaperdisplay_end_transaction(displayio_epaperdisplay_obj_t* self);
bool displayio_epaperdisplay_refresh(displayio_epaperdisplay_obj_t* self);
mp_float_t displayio_epaperdisplay_get_time_to_refresh(displayio_epaperdisplay_obj_t* self);
// The second point of the region is exclusive.
void displayio_epaperdisplay_set_region_to_update(displayio_epaperdisplay_obj_t* self, displayio_area_t* area);
bool displayio_epaperdisplay_frame_queued(displayio_epaperdisplay_obj_t* self);
bool displayio_epaperdisplay_refresh_queued(displayio_epaperdisplay_obj_t* self);
void displayio_epaperdisplay_finish_refresh(displayio_epaperdisplay_obj_t* self);
void displayio_epaperdisplay_send_pixels(displayio_epaperdisplay_obj_t* self, uint8_t* pixels, uint32_t length);

@ -221,10 +221,6 @@ bool common_hal_displayio_display_show(displayio_display_obj_t* self, displayio_
return true;
}
void common_hal_displayio_display_refresh_soon(displayio_display_obj_t* self) {
self->refresh = true;
}
const displayio_area_t* displayio_display_get_refresh_areas(displayio_display_obj_t *self) {
if (self->full_refresh) {
self->area.next = NULL;
@ -246,6 +242,118 @@ int32_t common_hal_displayio_display_wait_for_frame(displayio_display_obj_t* sel
return 0;
}
STATIC bool refresh_area(displayio_display_obj_t* display, const displayio_area_t* area) {
uint16_t buffer_size = 128; // In uint32_ts
displayio_area_t clipped;
// Clip the area to the display by overlapping the areas. If there is no overlap then we're done.
if (!displayio_display_clip_area(display, area, &clipped)) {
return true;
}
uint16_t subrectangles = 1;
uint16_t rows_per_buffer = displayio_area_height(&clipped);
uint8_t pixels_per_word = (sizeof(uint32_t) * 8) / display->colorspace.depth;
uint16_t pixels_per_buffer = displayio_area_size(&clipped);
if (displayio_area_size(&clipped) > buffer_size * pixels_per_word) {
rows_per_buffer = buffer_size * pixels_per_word / displayio_area_width(&clipped);
if (rows_per_buffer == 0) {
rows_per_buffer = 1;
}
// If pixels are packed by column then ensure rows_per_buffer is on a byte boundary.
if (display->colorspace.depth < 8 && !display->colorspace.pixels_in_byte_share_row) {
uint8_t pixels_per_byte = 8 / display->colorspace.depth;
if (rows_per_buffer % pixels_per_byte != 0) {
rows_per_buffer -= rows_per_buffer % pixels_per_byte;
}
}
subrectangles = displayio_area_height(&clipped) / rows_per_buffer;
if (displayio_area_height(&clipped) % rows_per_buffer != 0) {
subrectangles++;
}
pixels_per_buffer = rows_per_buffer * displayio_area_width(&clipped);
buffer_size = pixels_per_buffer / pixels_per_word;
if (pixels_per_buffer % pixels_per_word) {
buffer_size += 1;
}
}
// Allocated and shared as a uint32_t array so the compiler knows the
// alignment everywhere.
uint32_t buffer[buffer_size];
volatile uint32_t mask_length = (pixels_per_buffer / 32) + 1;
uint32_t mask[mask_length];
uint16_t remaining_rows = displayio_area_height(&clipped);
for (uint16_t j = 0; j < subrectangles; j++) {
displayio_area_t subrectangle = {
.x1 = clipped.x1,
.y1 = clipped.y1 + rows_per_buffer * j,
.x2 = clipped.x2,
.y2 = clipped.y1 + rows_per_buffer * (j + 1)
};
if (remaining_rows < rows_per_buffer) {
subrectangle.y2 = subrectangle.y1 + remaining_rows;
}
remaining_rows -= rows_per_buffer;
displayio_display_begin_transaction(display);
displayio_display_set_region_to_update(display, &subrectangle);
displayio_display_end_transaction(display);
uint16_t subrectangle_size_bytes;
if (display->colorspace.depth >= 8) {
subrectangle_size_bytes = displayio_area_size(&subrectangle) * (display->colorspace.depth / 8);
} else {
subrectangle_size_bytes = displayio_area_size(&subrectangle) / (8 / display->colorspace.depth);
}
for (uint16_t k = 0; k < mask_length; k++) {
mask[k] = 0x00000000;
}
for (uint16_t k = 0; k < buffer_size; k++) {
buffer[k] = 0x00000000;
}
displayio_display_fill_area(display, &subrectangle, mask, buffer);
if (!displayio_display_begin_transaction(display)) {
// Can't acquire display bus; skip the rest of the data. Try next display.
return false;
}
displayio_display_send_pixels(display, (uint8_t*) buffer, subrectangle_size_bytes);
displayio_display_end_transaction(display);
// TODO(tannewt): Make refresh displays faster so we don't starve other
// background tasks.
usb_background();
}
return true;
}
STATIC void refresh_display(displayio_display_obj_t* self) {
if (!displayio_display_begin_transaction(self)) {
// Can't acquire display bus; skip updating this display. Try next display.
continue;
}
displayio_display_end_transaction(self);
displayio_display_start_refresh(self);
const displayio_area_t* current_area = displayio_display_get_refresh_areas(self);
while (current_area != NULL) {
refresh_area(self, current_area);
current_area = current_area->next;
}
displayio_display_finish_refresh(self);
}
void common_hal_displayio_display_refresh(displayio_display_obj_t* self) {
// Time to refresh at specified frame rate?
while (!displayio_display_frame_queued(self)) {
// Too soon. Try next display.
continue;
}
refresh_display(self);
}
bool common_hal_displayio_display_get_auto_brightness(displayio_display_obj_t* self) {
return self->auto_brightness;
}
@ -411,6 +519,14 @@ void displayio_display_update_backlight(displayio_display_obj_t* self) {
self->last_backlight_refresh = ticks_ms;
}
void displayio_display_background(displayio_display_obj_t* self) {
displayio_display_update_backlight(self);
if (self->auto_refresh && (ticks_ms - self->last_refresh) > 16) {
display_refresh(self);
}
}
void release_display(displayio_display_obj_t* self) {
if (self->current_group != NULL) {
self->current_group->in_group = false;

@ -63,7 +63,7 @@ typedef struct {
uint8_t set_column_command;
uint8_t set_row_command;
uint8_t write_ram_command;
bool refresh;
bool auto_refresh;
bool single_byte_bounds;
bool data_as_commands;
bool auto_brightness;
@ -71,11 +71,7 @@ typedef struct {
bool full_refresh; // New group means we need to refresh the whole display.
} displayio_display_obj_t;
void displayio_display_start_refresh(displayio_display_obj_t* self);
const displayio_area_t* displayio_display_get_refresh_areas(displayio_display_obj_t *self);
bool displayio_display_fill_area(displayio_display_obj_t *self, displayio_area_t* area, uint32_t* mask, uint32_t *buffer);
void displayio_display_update_backlight(displayio_display_obj_t* self);
bool displayio_display_clip_area(displayio_display_obj_t *self, const displayio_area_t* area, displayio_area_t* clipped);
void displayio_display_background(displayio_display_obj_t* self);
void release_display(displayio_display_obj_t* self);
#endif // MICROPY_INCLUDED_SHARED_MODULE_DISPLAYIO_DISPLAY_H

@ -48,7 +48,7 @@ void common_hal_displayio_epaperdisplay_construct(displayio_epaperdisplay_obj_t*
int16_t colstart, int16_t rowstart, uint16_t rotation,
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 third_color, uint16_t refresh_display_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 refresh_display_command,
const mcu_pin_obj_t* busy_pin, bool busy_state, mp_float_t seconds_per_frame, bool always_toggle_chip_select) {
self->colorspace.depth = 1;
self->colorspace.grayscale = true;
@ -56,10 +56,10 @@ void common_hal_displayio_epaperdisplay_construct(displayio_epaperdisplay_obj_t*
self->colorspace.bytes_per_cell = 1;
self->colorspace.reverse_pixels_in_byte = true;
if (third_color != 0x000000) {
if (highlight_color != 0x000000) {
self->colorspace.tricolor = true;
self->colorspace.tricolor_hue = displayio_colorconverter_compute_hue(third_color);
self->colorspace.tricolor_luma = displayio_colorconverter_compute_luma(third_color);
self->colorspace.tricolor_hue = displayio_colorconverter_compute_hue(highlight_color);
self->colorspace.tricolor_luma = displayio_colorconverter_compute_luma(highlight_color);
}
self->set_column_window_command = set_column_window_command;
@ -174,6 +174,11 @@ void common_hal_displayio_epaperdisplay_construct(displayio_epaperdisplay_obj_t*
never_reset_pin_number(busy_pin->number);
}
// Clear the color memory if it isn't in use.
if (highlight_color == 0x00 && write_color_ram_command != NO_COMMAND) {
// TODO: Clear
}
// Set the group after initialization otherwise we may send pixels while we delay in
// initialization.
common_hal_displayio_epaperdisplay_show(self, &circuitpython_splash);
@ -198,14 +203,9 @@ bool common_hal_displayio_epaperdisplay_show(displayio_epaperdisplay_obj_t* self
self->current_group = root_group;
}
self->full_refresh = true;
common_hal_displayio_epaperdisplay_refresh_soon(self);
return true;
}
void common_hal_displayio_epaperdisplay_refresh_soon(displayio_epaperdisplay_obj_t* self) {
self->refresh = true;
}
const displayio_area_t* displayio_epaperdisplay_get_refresh_areas(displayio_epaperdisplay_obj_t *self) {
const displayio_area_t* first_area;
if (self->current_group == NULL || self->current_group->base.type != &displayio_group_type) {
@ -359,21 +359,23 @@ void displayio_epaperdisplay_start_refresh(displayio_epaperdisplay_obj_t* self)
self->last_refresh = ticks_ms;
}
bool displayio_epaperdisplay_frame_queued(displayio_epaperdisplay_obj_t* self) {
void displayio_epaperdisplay_background_task(displayio_epaperdisplay_obj_t* self) {
if (self->refreshing && self->busy.base.type == &digitalio_digitalinout_type) {
if (common_hal_digitalio_digitalinout_get_value(&self->busy) != self->busy_state) {
self->refreshing = false;
// Run stop sequence but don't wait for busy because busy is set when sleeping.
send_command_sequence(self, false, self->stop_sequence, self->stop_sequence_len);
} else {
return false;
}
}
if (self->current_group == NULL) {
return false;
}
}
uint32_t common_hal_displayio_epaperdisplay_get_time_to_refresh(displayio_epaperdisplay_obj_t* self) {
// Refresh at seconds per frame rate.
return (ticks_ms - self->last_refresh) > self->milliseconds_per_frame;
uint32_t elapsed_time = ticks_ms - self->last_refresh;
if (elapsed_time > self->milliseconds_per_frame) {
return 0;
}
return self->milliseconds_per_frame - elapsed_time;
}
void displayio_epaperdisplay_finish_refresh(displayio_epaperdisplay_obj_t* self) {
@ -394,7 +396,6 @@ void displayio_epaperdisplay_finish_refresh(displayio_epaperdisplay_obj_t* self)
void displayio_epaperdisplay_send_pixels(displayio_epaperdisplay_obj_t* self, uint8_t* pixels, uint32_t length) {
}
bool displayio_epaperdisplay_refresh_area(displayio_epaperdisplay_obj_t* self, const displayio_area_t* area) {
uint16_t buffer_size = 128; // In uint32_ts
@ -500,6 +501,42 @@ bool displayio_epaperdisplay_refresh_area(displayio_epaperdisplay_obj_t* self, c
return true;
}
bool common_hal_displayio_epaperdisplay_refresh(displayio_epaperdisplay_obj_t* self) {
if (self->refreshing && self->busy.base.type == &digitalio_digitalinout_type) {
if (common_hal_digitalio_digitalinout_get_value(&self->busy) != self->busy_state) {
self->refreshing = false;
// Run stop sequence but don't wait for busy because busy is set when sleeping.
send_command_sequence(self, false, self->stop_sequence, self->stop_sequence_len);
} else {
return false;
}
}
if (self->current_group == NULL) {
return false;
}
// Refresh at seconds per frame rate.
if (ticks_ms - self->last_refresh) > self->milliseconds_per_frame;
if (displayio_epaperdisplay_get_time_to_refresh(display) > 0) {
return false;
}
if (!displayio_epaperdisplay_bus_free(display)) {
// Can't acquire display bus; skip updating this display. Try next display.
continue;
}
const displayio_area_t* current_area = displayio_epaperdisplay_get_refresh_areas(display);
if (current_area == NULL) {
continue;
}
displayio_epaperdisplay_start_refresh(display);
while (current_area != NULL) {
displayio_epaperdisplay_refresh_area(display, current_area);
current_area = current_area->next;
}
displayio_epaperdisplay_finish_refresh(display);
}
void release_epaperdisplay(displayio_epaperdisplay_obj_t* self) {
if (self->current_group != NULL) {
self->current_group->in_group = false;

@ -75,18 +75,12 @@ typedef struct {
bool busy_state;
bool black_bits_inverted;
bool color_bits_inverted;
bool refresh;
bool refreshing;
bool full_refresh; // New group means we need to refresh the whole display.
bool always_toggle_chip_select;
} displayio_epaperdisplay_obj_t;
bool displayio_epaperdisplay_refresh_area(displayio_epaperdisplay_obj_t* display, const displayio_area_t* area);
void displayio_epaperdisplay_start_refresh(displayio_epaperdisplay_obj_t* self);
const displayio_area_t* displayio_epaperdisplay_get_refresh_areas(displayio_epaperdisplay_obj_t *self);
bool displayio_epaperdisplay_fill_area(displayio_epaperdisplay_obj_t *self, displayio_area_t* area, uint32_t* mask, uint32_t *buffer);
bool displayio_epaperdisplay_clip_area(displayio_epaperdisplay_obj_t *self, const displayio_area_t* area, displayio_area_t* clipped);
bool displayio_epaperdisplay_bus_free(displayio_epaperdisplay_obj_t *self);
void displayio_epaperdisplay_background(displayio_epaperdisplay_obj_t* self);
void release_epaperdisplay(displayio_epaperdisplay_obj_t* self);
#endif // MICROPY_INCLUDED_SHARED_MODULE_DISPLAYIO_EPAPERDISPLAY_H

@ -19,100 +19,11 @@
#include "supervisor/usb.h"
primary_display_t displays[CIRCUITPY_DISPLAY_LIMIT];
uint32_t frame_count = 0;
bool refresh_area(displayio_display_obj_t* display, const displayio_area_t* area) {
uint16_t buffer_size = 128; // In uint32_ts
// Check for recursive calls to displayio_background.
bool displayio_background_in_progress = false;
displayio_area_t clipped;
// Clip the area to the display by overlapping the areas. If there is no overlap then we're done.
if (!displayio_display_clip_area(display, area, &clipped)) {
return true;
}
uint16_t subrectangles = 1;
uint16_t rows_per_buffer = displayio_area_height(&clipped);
uint8_t pixels_per_word = (sizeof(uint32_t) * 8) / display->colorspace.depth;
uint16_t pixels_per_buffer = displayio_area_size(&clipped);
if (displayio_area_size(&clipped) > buffer_size * pixels_per_word) {
rows_per_buffer = buffer_size * pixels_per_word / displayio_area_width(&clipped);
if (rows_per_buffer == 0) {
rows_per_buffer = 1;
}
// If pixels are packed by column then ensure rows_per_buffer is on a byte boundary.
if (display->colorspace.depth < 8 && !display->colorspace.pixels_in_byte_share_row) {
uint8_t pixels_per_byte = 8 / display->colorspace.depth;
if (rows_per_buffer % pixels_per_byte != 0) {
rows_per_buffer -= rows_per_buffer % pixels_per_byte;
}
}
subrectangles = displayio_area_height(&clipped) / rows_per_buffer;
if (displayio_area_height(&clipped) % rows_per_buffer != 0) {
subrectangles++;
}
pixels_per_buffer = rows_per_buffer * displayio_area_width(&clipped);
buffer_size = pixels_per_buffer / pixels_per_word;
if (pixels_per_buffer % pixels_per_word) {
buffer_size += 1;
}
}
// Allocated and shared as a uint32_t array so the compiler knows the
// alignment everywhere.
uint32_t buffer[buffer_size];
volatile uint32_t mask_length = (pixels_per_buffer / 32) + 1;
uint32_t mask[mask_length];
uint16_t remaining_rows = displayio_area_height(&clipped);
for (uint16_t j = 0; j < subrectangles; j++) {
displayio_area_t subrectangle = {
.x1 = clipped.x1,
.y1 = clipped.y1 + rows_per_buffer * j,
.x2 = clipped.x2,
.y2 = clipped.y1 + rows_per_buffer * (j + 1)
};
if (remaining_rows < rows_per_buffer) {
subrectangle.y2 = subrectangle.y1 + remaining_rows;
}
remaining_rows -= rows_per_buffer;
displayio_display_begin_transaction(display);
displayio_display_set_region_to_update(display, &subrectangle);
displayio_display_end_transaction(display);
uint16_t subrectangle_size_bytes;
if (display->colorspace.depth >= 8) {
subrectangle_size_bytes = displayio_area_size(&subrectangle) * (display->colorspace.depth / 8);
} else {
subrectangle_size_bytes = displayio_area_size(&subrectangle) / (8 / display->colorspace.depth);
}
for (uint16_t k = 0; k < mask_length; k++) {
mask[k] = 0x00000000;
}
for (uint16_t k = 0; k < buffer_size; k++) {
buffer[k] = 0x00000000;
}
displayio_display_fill_area(display, &subrectangle, mask, buffer);
if (!displayio_display_begin_transaction(display)) {
// Can't acquire display bus; skip the rest of the data. Try next display.
return false;
}
displayio_display_send_pixels(display, (uint8_t*) buffer, subrectangle_size_bytes);
displayio_display_end_transaction(display);
// TODO(tannewt): Make refresh displays faster so we don't starve other
// background tasks.
usb_background();
}
return true;
}
// Check for recursive calls to displayio_refresh_displays.
bool refresh_displays_in_progress = false;
void displayio_refresh_displays(void) {
void displayio_background(void) {
if (mp_hal_is_interrupted()) {
return;
}
@ -121,12 +32,12 @@ void displayio_refresh_displays(void) {
return;
}
if (refresh_displays_in_progress) {
if (displayio_background_in_progress) {
// Don't allow recursive calls to this routine.
return;
}
refresh_displays_in_progress = true;
displayio_background_in_progress = true;
for (uint8_t i = 0; i < CIRCUITPY_DISPLAY_LIMIT; i++) {
if (displays[i].display.base.type == NULL || displays[i].display.base.type == &mp_type_NoneType) {
@ -134,54 +45,14 @@ void displayio_refresh_displays(void) {
continue;
}
if (displays[i].display.base.type == &displayio_display_type) {
displayio_display_obj_t* display = &displays[i].display;
displayio_display_update_backlight(display);
// Time to refresh at specified frame rate?
if (!displayio_display_frame_queued(display)) {
// Too soon. Try next display.
continue;
}
if (!displayio_display_begin_transaction(display)) {
// Can't acquire display bus; skip updating this display. Try next display.
continue;
}
displayio_display_end_transaction(display);
displayio_display_start_refresh(display);
const displayio_area_t* current_area = displayio_display_get_refresh_areas(display);
while (current_area != NULL) {
refresh_area(display, current_area);
current_area = current_area->next;
}
displayio_display_finish_refresh(display);
} else if (displays[i].epaper_display.base.type == &displayio_epaperdisplay_type) {
displayio_epaperdisplay_obj_t* display = &displays[i].epaper_display;
// Time to refresh at specified frame rate?
if (!displayio_epaperdisplay_frame_queued(display)) {
// Too soon. Try next display.
continue;
}
if (!displayio_epaperdisplay_bus_free(display)) {
// Can't acquire display bus; skip updating this display. Try next display.
continue;
}
const displayio_area_t* current_area = displayio_epaperdisplay_get_refresh_areas(display);
if (current_area == NULL) {
continue;
}
displayio_epaperdisplay_start_refresh(display);
while (current_area != NULL) {
displayio_epaperdisplay_refresh_area(display, current_area);
current_area = current_area->next;
}
displayio_epaperdisplay_finish_refresh(display);
displayio_display_background(&displays[i].display);
} else if (displays[i].epaperdisplay.base.type == &displayio_epaperdisplay_type) {
displayio_epaperdisplay_background(&displays[i].epaperdisplay);
}
frame_count++;
}
// All done.
refresh_displays_in_progress = false;
displayio_background_in_progress = false;
}
void common_hal_displayio_release_displays(void) {
@ -245,7 +116,7 @@ void reset_displays(void) {
((uint32_t) i2c->bus) > ((uint32_t) &displays + CIRCUITPY_DISPLAY_LIMIT)) {
busio_i2c_obj_t* original_i2c = i2c->bus;
#if BOARD_I2C
// We don't need to move original_i2c if it is the board.SPI object because it is
// We don't need to move original_i2c if it is the board.I2C object because it is
// statically allocated already. (Doing so would also make it impossible to reference in
// a subsequent VM run.)
if (original_i2c == common_hal_board_get_i2c()) {

@ -50,7 +50,7 @@ extern primary_display_t displays[CIRCUITPY_DISPLAY_LIMIT];
extern displayio_group_t circuitpython_splash;
void displayio_refresh_displays(void);
void displayio_background(void);
void reset_displays(void);
void displayio_gc_collect(void);

Loading…
Cancel
Save