From c247e7df9c043dc6a452a6cabc7bf232c0e9e085 Mon Sep 17 00:00:00 2001 From: Scott Shawcroft Date: Wed, 14 Aug 2019 14:17:35 -0700 Subject: Begin refresh rework. --- shared-module/displayio/Display.c | 124 +++++++++++++++++++++++++- shared-module/displayio/Display.h | 8 +- shared-module/displayio/EPaperDisplay.c | 71 +++++++++++---- shared-module/displayio/EPaperDisplay.h | 8 +- shared-module/displayio/__init__.c | 149 +++----------------------------- shared-module/displayio/__init__.h | 2 +- 6 files changed, 188 insertions(+), 174 deletions(-) (limited to 'shared-module') diff --git a/shared-module/displayio/Display.c b/shared-module/displayio/Display.c index 97a18740a..fafb35f43 100644 --- a/shared-module/displayio/Display.c +++ b/shared-module/displayio/Display.c @@ -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; diff --git a/shared-module/displayio/Display.h b/shared-module/displayio/Display.h index c85d69b0e..db0a23012 100644 --- a/shared-module/displayio/Display.h +++ b/shared-module/displayio/Display.h @@ -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 diff --git a/shared-module/displayio/EPaperDisplay.c b/shared-module/displayio/EPaperDisplay.c index 5e4bdc340..434adbdf4 100644 --- a/shared-module/displayio/EPaperDisplay.c +++ b/shared-module/displayio/EPaperDisplay.c @@ -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; diff --git a/shared-module/displayio/EPaperDisplay.h b/shared-module/displayio/EPaperDisplay.h index 737b5987a..cc7db6f6d 100644 --- a/shared-module/displayio/EPaperDisplay.h +++ b/shared-module/displayio/EPaperDisplay.h @@ -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 diff --git a/shared-module/displayio/__init__.c b/shared-module/displayio/__init__.c index a8f888654..0ebed58c2 100644 --- a/shared-module/displayio/__init__.c +++ b/shared-module/displayio/__init__.c @@ -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()) { diff --git a/shared-module/displayio/__init__.h b/shared-module/displayio/__init__.h index 95eefbdd9..e78bc61ce 100644 --- a/shared-module/displayio/__init__.h +++ b/shared-module/displayio/__init__.h @@ -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); -- cgit v1.2.3