Rework refresh API and factor common display stuff out
NOT TESTED! Just compiles Fixes #1691
This commit is contained in:
parent
c247e7df9c
commit
36a23e0fe3
|
@ -289,7 +289,8 @@ SRC_COMMON_HAL_EXPANDED = $(addprefix shared-bindings/, $(SRC_COMMON_HAL)) \
|
|||
$(addprefix common-hal/, $(SRC_COMMON_HAL))
|
||||
|
||||
SRC_SHARED_MODULE_EXPANDED = $(addprefix shared-bindings/, $(SRC_SHARED_MODULE)) \
|
||||
$(addprefix shared-module/, $(SRC_SHARED_MODULE))
|
||||
$(addprefix shared-module/, $(SRC_SHARED_MODULE)) \
|
||||
$(addprefix shared-module/, $(SRC_SHARED_MODULE_INTERNAL))
|
||||
|
||||
SRC_S = supervisor/$(CHIP_FAMILY)_cpu.s
|
||||
|
||||
|
|
|
@ -106,7 +106,9 @@ void board_init(void) {
|
|||
1.0f, // brightness (ignored)
|
||||
true, // auto_brightness
|
||||
false, // single_byte_bounds
|
||||
false); // data_as_commands
|
||||
false, // data_as_commands
|
||||
true, // auto_refresh
|
||||
60); // native_frames_per_second
|
||||
}
|
||||
|
||||
bool board_requests_safe_mode(void) {
|
||||
|
|
|
@ -108,7 +108,9 @@ void board_init(void) {
|
|||
1.0f, // brightness (ignored)
|
||||
true, // auto_brightness
|
||||
false, // single_byte_bounds
|
||||
false); // data_as_commands
|
||||
false, // data_as_commands
|
||||
true, // auto_refresh
|
||||
60); // native_frames_per_second
|
||||
}
|
||||
|
||||
bool board_requests_safe_mode(void) {
|
||||
|
|
|
@ -86,7 +86,9 @@ void board_init(void) {
|
|||
1.0f, // brightness (ignored)
|
||||
true, // auto_brightness
|
||||
false, // single_byte_bounds
|
||||
false); // data_as_commands
|
||||
false, // data_as_commands
|
||||
true, // auto_refresh
|
||||
60); // native_frames_per_second
|
||||
}
|
||||
|
||||
bool board_requests_safe_mode(void) {
|
||||
|
|
|
@ -108,7 +108,9 @@ void board_init(void) {
|
|||
1.0f, // brightness (ignored)
|
||||
true, // auto_brightness
|
||||
false, // single_byte_bounds
|
||||
false); // data_as_commands
|
||||
false, // data_as_commands
|
||||
true, // auto_refresh
|
||||
60); // native_frames_per_second
|
||||
}
|
||||
|
||||
bool board_requests_safe_mode(void) {
|
||||
|
|
|
@ -86,7 +86,9 @@ void board_init(void) {
|
|||
1.0f, // brightness (ignored)
|
||||
true, // auto_brightness
|
||||
false, // single_byte_bounds
|
||||
false); // data_as_commands
|
||||
false, // data_as_commands
|
||||
true, // auto_refresh
|
||||
60); // native_frames_per_second
|
||||
}
|
||||
|
||||
bool board_requests_safe_mode(void) {
|
||||
|
|
|
@ -98,7 +98,9 @@ void board_init(void) {
|
|||
1.0f, // brightness (ignored)
|
||||
true, // auto_brightness
|
||||
false, // single_byte_bounds
|
||||
false); // data_as_commands
|
||||
false, // data_as_commands
|
||||
true, // auto_refresh
|
||||
60); // native_frames_per_second
|
||||
}
|
||||
|
||||
bool board_requests_safe_mode(void) {
|
||||
|
|
|
@ -118,7 +118,9 @@ void board_init(void) {
|
|||
1.0f, // brightness (ignored)
|
||||
true, // auto_brightness
|
||||
false, // single_byte_bounds
|
||||
false); // data_as_commands
|
||||
false, // data_as_commands
|
||||
true, // auto_refresh
|
||||
60); // native_frames_per_second
|
||||
}
|
||||
|
||||
bool board_requests_safe_mode(void) {
|
||||
|
|
|
@ -192,7 +192,8 @@ SRC_COMMON_HAL_EXPANDED = $(addprefix shared-bindings/, $(SRC_COMMON_HAL)) \
|
|||
$(addprefix common-hal/, $(SRC_COMMON_HAL))
|
||||
|
||||
SRC_SHARED_MODULE_EXPANDED = $(addprefix shared-bindings/, $(SRC_SHARED_MODULE)) \
|
||||
$(addprefix shared-module/, $(SRC_SHARED_MODULE))
|
||||
$(addprefix shared-module/, $(SRC_SHARED_MODULE)) \
|
||||
$(addprefix shared-module/, $(SRC_SHARED_MODULE_INTERNAL))
|
||||
|
||||
SRC_S = supervisor/cpu.s
|
||||
|
||||
|
|
|
@ -216,7 +216,8 @@ SRC_COMMON_HAL_EXPANDED = $(addprefix shared-bindings/, $(SRC_COMMON_HAL)) \
|
|||
$(addprefix common-hal/, $(SRC_COMMON_HAL))
|
||||
|
||||
SRC_SHARED_MODULE_EXPANDED = $(addprefix shared-bindings/, $(SRC_SHARED_MODULE)) \
|
||||
$(addprefix shared-module/, $(SRC_SHARED_MODULE))
|
||||
$(addprefix shared-module/, $(SRC_SHARED_MODULE)) \
|
||||
$(addprefix shared-module/, $(SRC_SHARED_MODULE_INTERNAL))
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -364,6 +364,12 @@ SRC_SHARED_MODULE_ALL += \
|
|||
touchio/__init__.c
|
||||
endif
|
||||
|
||||
# All possible sources are listed here, and are filtered by SRC_PATTERNS.
|
||||
SRC_SHARED_MODULE_INTERNAL = \
|
||||
$(filter $(SRC_PATTERNS), \
|
||||
displayio/display_core.c \
|
||||
)
|
||||
|
||||
ifeq ($(INTERNAL_LIBM),1)
|
||||
SRC_LIBM = \
|
||||
$(addprefix lib/,\
|
||||
|
|
|
@ -30,6 +30,7 @@
|
|||
#include "shared-bindings/busio/SPI.h"
|
||||
#include "shared-bindings/displayio/Display.h"
|
||||
#include "shared-module/_stage/__init__.h"
|
||||
#include "shared-module/displayio/display_core.h"
|
||||
#include "Layer.h"
|
||||
#include "Text.h"
|
||||
|
||||
|
@ -95,7 +96,8 @@ STATIC mp_obj_t stage_render(size_t n_args, const mp_obj_t *args) {
|
|||
scale = mp_obj_get_int(args[7]);
|
||||
}
|
||||
|
||||
while (!displayio_display_begin_transaction(display)) {
|
||||
// TODO: Everything below should be in shared-module because it's not argument parsing.
|
||||
while (!displayio_display_core_begin_transaction(&display->core)) {
|
||||
RUN_BACKGROUND_TASKS;
|
||||
}
|
||||
displayio_area_t area;
|
||||
|
@ -103,12 +105,12 @@ STATIC mp_obj_t stage_render(size_t n_args, const mp_obj_t *args) {
|
|||
area.y1 = y0;
|
||||
area.x2 = x1;
|
||||
area.y2 = y1;
|
||||
displayio_display_set_region_to_update(display, &area);
|
||||
displayio_display_core_set_region_to_update(&display->core, display->set_column_command, display->set_row_command, NO_COMMAND, NO_COMMAND, display->data_as_commands, false, &area);
|
||||
|
||||
display->send(display->bus, true, &display->write_ram_command, 1);
|
||||
display->core.send(display->core.bus, true, true, &display->write_ram_command, 1);
|
||||
render_stage(x0, y0, x1, y1, layers, layers_size, buffer, buffer_size,
|
||||
display, scale);
|
||||
displayio_display_end_transaction(display);
|
||||
displayio_display_core_end_transaction(&display->core);
|
||||
|
||||
return mp_const_none;
|
||||
}
|
||||
|
|
|
@ -51,7 +51,7 @@
|
|||
//| Most people should not use this class directly. Use a specific display driver instead that will
|
||||
//| contain the initialization sequence at minimum.
|
||||
//|
|
||||
//| .. class:: Display(display_bus, init_sequence, *, width, height, colstart=0, rowstart=0, rotation=0, color_depth=16, grayscale=False, pixels_in_byte_share_row=True, bytes_per_cell=1, reverse_pixels_in_byte=False, set_column_command=0x2a, set_row_command=0x2b, write_ram_command=0x2c, set_vertical_scroll=0, backlight_pin=None, brightness_command=None, brightness=1.0, auto_brightness=False, single_byte_bounds=False, data_as_commands=False)
|
||||
//| .. class:: Display(display_bus, init_sequence, *, width, height, colstart=0, rowstart=0, rotation=0, color_depth=16, grayscale=False, pixels_in_byte_share_row=True, bytes_per_cell=1, reverse_pixels_in_byte=False, set_column_command=0x2a, set_row_command=0x2b, write_ram_command=0x2c, set_vertical_scroll=0, backlight_pin=None, brightness_command=None, brightness=1.0, auto_brightness=False, single_byte_bounds=False, data_as_commands=False, auto_refresh=True, native_frames_per_second=60)
|
||||
//|
|
||||
//| Create a Display object on the given display bus (`displayio.FourWire` or `displayio.ParallelBus`).
|
||||
//|
|
||||
|
@ -102,9 +102,11 @@
|
|||
//| :param bool auto_brightness: If True, brightness is controlled via an ambient light sensor or other mechanism.
|
||||
//| :param bool single_byte_bounds: Display column and row commands use single bytes
|
||||
//| :param bool data_as_commands: Treat all init and boundary data as SPI commands. Certain displays require this.
|
||||
//| :param bool auto_refresh: Automatically refresh the screen
|
||||
//| :param int native_frames_per_second: Number of display refreshes per second that occur with the given init_sequence.
|
||||
//|
|
||||
STATIC mp_obj_t displayio_display_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_init_sequence, ARG_width, ARG_height, ARG_colstart, ARG_rowstart, ARG_rotation, ARG_color_depth, ARG_grayscale, ARG_pixels_in_byte_share_row, ARG_bytes_per_cell, ARG_reverse_pixels_in_byte, ARG_set_column_command, ARG_set_row_command, ARG_write_ram_command, ARG_set_vertical_scroll, ARG_backlight_pin, ARG_brightness_command, ARG_brightness, ARG_auto_brightness, ARG_single_byte_bounds, ARG_data_as_commands };
|
||||
enum { ARG_display_bus, ARG_init_sequence, ARG_width, ARG_height, ARG_colstart, ARG_rowstart, ARG_rotation, ARG_color_depth, ARG_grayscale, ARG_pixels_in_byte_share_row, ARG_bytes_per_cell, ARG_reverse_pixels_in_byte, ARG_set_column_command, ARG_set_row_command, ARG_write_ram_command, ARG_set_vertical_scroll, ARG_backlight_pin, ARG_brightness_command, ARG_brightness, ARG_auto_brightness, ARG_single_byte_bounds, ARG_data_as_commands, ARG_auto_refresh, ARG_native_frames_per_second };
|
||||
static const mp_arg_t allowed_args[] = {
|
||||
{ MP_QSTR_display_bus, MP_ARG_REQUIRED | MP_ARG_OBJ },
|
||||
{ MP_QSTR_init_sequence, MP_ARG_REQUIRED | MP_ARG_OBJ },
|
||||
|
@ -128,6 +130,8 @@ STATIC mp_obj_t displayio_display_make_new(const mp_obj_type_t *type, size_t n_a
|
|||
{ MP_QSTR_auto_brightness, MP_ARG_BOOL | MP_ARG_KW_ONLY, {.u_bool = false} },
|
||||
{ MP_QSTR_single_byte_bounds, MP_ARG_BOOL | MP_ARG_KW_ONLY, {.u_bool = false} },
|
||||
{ MP_QSTR_data_as_commands, MP_ARG_BOOL | MP_ARG_KW_ONLY, {.u_bool = false} },
|
||||
{ MP_QSTR_auto_refresh, MP_ARG_BOOL | MP_ARG_KW_ONLY, {.u_bool = true} },
|
||||
{ MP_QSTR_native_frames_per_second, MP_ARG_INT | MP_ARG_KW_ONLY, {.u_int = 60} },
|
||||
};
|
||||
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
|
||||
mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
|
||||
|
@ -178,7 +182,9 @@ STATIC mp_obj_t displayio_display_make_new(const mp_obj_type_t *type, size_t n_a
|
|||
brightness,
|
||||
args[ARG_auto_brightness].u_bool,
|
||||
args[ARG_single_byte_bounds].u_bool,
|
||||
args[ARG_data_as_commands].u_bool
|
||||
args[ARG_data_as_commands].u_bool,
|
||||
args[ARG_auto_refresh].u_bool,
|
||||
args[ARG_native_frames_per_second].u_int
|
||||
);
|
||||
|
||||
return self;
|
||||
|
@ -214,14 +220,28 @@ MP_DEFINE_CONST_FUN_OBJ_2(displayio_display_show_obj, displayio_display_obj_show
|
|||
|
||||
//| .. method:: refresh(*, target_frames_per_second=None, minimum_frames_per_second=1)
|
||||
//|
|
||||
//| 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.
|
||||
//| When auto refresh is off, 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(mp_obj_t self_in) {
|
||||
displayio_display_obj_t *self = native_display(self_in);
|
||||
common_hal_displayio_display_refresh(self);
|
||||
//| When auto refresh is on, updates the display immediately. (The display will also update
|
||||
//| without calls to this.)
|
||||
//|
|
||||
STATIC mp_obj_t displayio_display_obj_refresh(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
|
||||
enum { ARG_target_frames_per_second, ARG_minimum_frames_per_second };
|
||||
static const mp_arg_t allowed_args[] = {
|
||||
{ MP_QSTR_target_frames_per_second, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 60} },
|
||||
{ MP_QSTR_minimum_frames_per_second, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 1} },
|
||||
};
|
||||
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
|
||||
mp_arg_parse_all(n_args - 1, pos_args + 1, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
|
||||
|
||||
displayio_display_obj_t *self = native_display(pos_args[0]);
|
||||
common_hal_displayio_display_refresh(self, 1000 / args[ARG_target_frames_per_second].u_int, 1000 / args[ARG_minimum_frames_per_second].u_int);
|
||||
return mp_const_none;
|
||||
}
|
||||
MP_DEFINE_CONST_FUN_OBJ_1(displayio_display_refresh_soon_obj, displayio_display_obj_refresh_soon);
|
||||
MP_DEFINE_CONST_FUN_OBJ_KW(displayio_display_refresh_obj, 1, displayio_display_obj_refresh);
|
||||
|
||||
//| .. attribute:: auto_refresh
|
||||
//|
|
||||
|
@ -376,7 +396,7 @@ const mp_obj_property_t displayio_display_rotation_obj = {
|
|||
//|
|
||||
STATIC mp_obj_t displayio_display_obj_get_bus(mp_obj_t self_in) {
|
||||
displayio_display_obj_t *self = native_display(self_in);
|
||||
return self->bus;
|
||||
return common_hal_displayio_display_get_bus(self);
|
||||
}
|
||||
MP_DEFINE_CONST_FUN_OBJ_1(displayio_display_get_bus_obj, displayio_display_obj_get_bus);
|
||||
|
||||
|
@ -412,18 +432,18 @@ STATIC mp_obj_t displayio_display_obj_fill_row(size_t n_args, const mp_obj_t *po
|
|||
if (bufinfo.typecode != BYTEARRAY_TYPECODE) {
|
||||
mp_raise_ValueError(translate("Buffer is not a bytearray."));
|
||||
}
|
||||
if (self->colorspace.depth != 16) {
|
||||
if (self->core.colorspace.depth != 16) {
|
||||
mp_raise_ValueError(translate("Display must have a 16 bit colorspace."));
|
||||
}
|
||||
|
||||
displayio_area_t area = {
|
||||
.x1 = 0,
|
||||
.y1 = y,
|
||||
.x2 = self->width,
|
||||
.x2 = self->core.width,
|
||||
.y2 = y + 1
|
||||
};
|
||||
uint8_t pixels_per_word = (sizeof(uint32_t) * 8) / self->colorspace.depth;
|
||||
uint16_t buffer_size = self->width / pixels_per_word;
|
||||
uint8_t pixels_per_word = (sizeof(uint32_t) * 8) / self->core.colorspace.depth;
|
||||
uint16_t buffer_size = self->core.width / pixels_per_word;
|
||||
uint16_t pixels_per_buffer = displayio_area_size(&area);
|
||||
if (pixels_per_buffer % pixels_per_word) {
|
||||
buffer_size += 1;
|
||||
|
@ -440,7 +460,7 @@ STATIC mp_obj_t displayio_display_obj_fill_row(size_t n_args, const mp_obj_t *po
|
|||
mask[k] = 0x00000000;
|
||||
}
|
||||
|
||||
displayio_display_fill_area(self, &area, mask, result_buffer);
|
||||
displayio_display_core_fill_area(&self->core, &area, mask, result_buffer);
|
||||
return result;
|
||||
} else {
|
||||
mp_raise_ValueError(translate("Buffer is too small"));
|
||||
|
@ -448,13 +468,9 @@ STATIC mp_obj_t displayio_display_obj_fill_row(size_t n_args, const mp_obj_t *po
|
|||
}
|
||||
MP_DEFINE_CONST_FUN_OBJ_KW(displayio_display_fill_row_obj, 1, displayio_display_obj_fill_row);
|
||||
|
||||
|
||||
|
||||
|
||||
STATIC const mp_rom_map_elem_t displayio_display_locals_dict_table[] = {
|
||||
{ MP_ROM_QSTR(MP_QSTR_show), MP_ROM_PTR(&displayio_display_show_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_refresh_soon), MP_ROM_PTR(&displayio_display_refresh_soon_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_wait_for_frame), MP_ROM_PTR(&displayio_display_wait_for_frame_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_refresh), MP_ROM_PTR(&displayio_display_refresh_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) },
|
||||
|
|
|
@ -44,24 +44,29 @@ 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, bool auto_refresh, uint8_t frames_per_second,
|
||||
bool single_byte_bounds, bool data_as_commands);
|
||||
mp_float_t brightness, bool auto_brightness,
|
||||
bool single_byte_bounds, bool data_as_commands, bool auto_refresh, uint16_t native_frames_per_second);
|
||||
|
||||
bool common_hal_displayio_display_show(displayio_display_obj_t* self, displayio_group_t* root_group);
|
||||
bool common_hal_displayio_display_show(displayio_display_obj_t* self,
|
||||
displayio_group_t* root_group);
|
||||
|
||||
void common_hal_displayio_display_refresh(displayio_display_obj_t* self);
|
||||
// Times in ms.
|
||||
void common_hal_displayio_display_refresh(displayio_display_obj_t* self, uint32_t target_frame_time, uint32_t maximum_frame_time);
|
||||
|
||||
bool displayio_display_begin_transaction(displayio_display_obj_t* self);
|
||||
void displayio_display_end_transaction(displayio_display_obj_t* self);
|
||||
|
||||
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);
|
||||
bool common_hal_displayio_display_get_auto_refresh(displayio_display_obj_t* self);
|
||||
void common_hal_displayio_display_set_auto_refresh(displayio_display_obj_t* self, bool auto_refresh);
|
||||
|
||||
uint16_t common_hal_displayio_display_get_width(displayio_display_obj_t* self);
|
||||
uint16_t common_hal_displayio_display_get_height(displayio_display_obj_t* self);
|
||||
uint16_t common_hal_displayio_display_get_rotation(displayio_display_obj_t* self);
|
||||
|
||||
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);
|
||||
|
||||
mp_float_t common_hal_displayio_display_get_brightness(displayio_display_obj_t* self);
|
||||
bool common_hal_displayio_display_set_brightness(displayio_display_obj_t* self, mp_float_t brightness);
|
||||
|
||||
mp_obj_t common_hal_displayio_display_get_bus(displayio_display_obj_t* self);
|
||||
|
||||
|
||||
#endif // MICROPY_INCLUDED_SHARED_BINDINGS_DISPLAYIO_DISPLAY_H
|
||||
|
|
|
@ -208,7 +208,7 @@ MP_DEFINE_CONST_FUN_OBJ_2(displayio_epaperdisplay_show_obj, displayio_epaperdisp
|
|||
//| 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) {
|
||||
STATIC mp_obj_t displayio_epaperdisplay_obj_refresh(mp_obj_t self_in) {
|
||||
displayio_epaperdisplay_obj_t *self = native_display(self_in);
|
||||
bool ok = common_hal_displayio_epaperdisplay_refresh(self);
|
||||
if (!ok) {
|
||||
|
@ -216,7 +216,7 @@ STATIC mp_obj_t displayio_epaperdisplay_obj_refresh_soon(mp_obj_t self_in) {
|
|||
}
|
||||
return mp_const_none;
|
||||
}
|
||||
MP_DEFINE_CONST_FUN_OBJ_1(displayio_epaperdisplay_refresh_soon_obj, displayio_epaperdisplay_obj_refresh_soon);
|
||||
MP_DEFINE_CONST_FUN_OBJ_1(displayio_epaperdisplay_refresh_obj, displayio_epaperdisplay_obj_refresh);
|
||||
|
||||
//| .. attribute:: time_to_refresh
|
||||
//|
|
||||
|
@ -225,7 +225,7 @@ MP_DEFINE_CONST_FUN_OBJ_1(displayio_epaperdisplay_refresh_soon_obj, displayio_ep
|
|||
//|
|
||||
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_float(common_hal_displayio_epaperdisplay_get_time_to_refresh(self));
|
||||
return mp_obj_new_float(common_hal_displayio_epaperdisplay_get_time_to_refresh(self) / 1000.0);
|
||||
}
|
||||
MP_DEFINE_CONST_FUN_OBJ_1(displayio_epaperdisplay_get_time_to_refresh_obj, displayio_epaperdisplay_obj_get_time_to_refresh);
|
||||
|
||||
|
@ -279,7 +279,7 @@ const mp_obj_property_t displayio_epaperdisplay_height_obj = {
|
|||
//|
|
||||
STATIC mp_obj_t displayio_epaperdisplay_obj_get_bus(mp_obj_t self_in) {
|
||||
displayio_epaperdisplay_obj_t *self = native_display(self_in);
|
||||
return self->bus;
|
||||
return common_hal_displayio_epaperdisplay_get_bus(self);
|
||||
}
|
||||
MP_DEFINE_CONST_FUN_OBJ_1(displayio_epaperdisplay_get_bus_obj, displayio_epaperdisplay_obj_get_bus);
|
||||
|
||||
|
@ -293,7 +293,7 @@ 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_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_refresh), 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) },
|
||||
|
|
|
@ -46,23 +46,12 @@ void common_hal_displayio_epaperdisplay_construct(displayio_epaperdisplay_obj_t*
|
|||
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_refresh(displayio_epaperdisplay_obj_t* self);
|
||||
|
||||
bool common_hal_displayio_epaperdisplay_show(displayio_epaperdisplay_obj_t* self, displayio_group_t* root_group);
|
||||
|
||||
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);
|
||||
|
||||
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);
|
||||
// Returns time in milliseconds.
|
||||
uint32_t common_hal_displayio_epaperdisplay_get_time_to_refresh(displayio_epaperdisplay_obj_t* self);
|
||||
|
||||
bool common_hal_displayio_epaperdisplay_get_auto_brightness(displayio_epaperdisplay_obj_t* self);
|
||||
void common_hal_displayio_epaperdisplay_set_auto_brightness(displayio_epaperdisplay_obj_t* self, bool auto_brightness);
|
||||
|
@ -73,4 +62,6 @@ uint16_t common_hal_displayio_epaperdisplay_get_height(displayio_epaperdisplay_o
|
|||
mp_float_t common_hal_displayio_epaperdisplay_get_brightness(displayio_epaperdisplay_obj_t* self);
|
||||
bool common_hal_displayio_epaperdisplay_set_brightness(displayio_epaperdisplay_obj_t* self, mp_float_t brightness);
|
||||
|
||||
mp_obj_t common_hal_displayio_epaperdisplay_get_bus(displayio_epaperdisplay_obj_t* self);
|
||||
|
||||
#endif // MICROPY_INCLUDED_SHARED_BINDINGS_DISPLAYIO_EPAPERDISPLAY_H
|
||||
|
|
|
@ -57,7 +57,7 @@ void render_stage(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1,
|
|||
index += 1;
|
||||
// The buffer is full, send it.
|
||||
if (index >= buffer_size) {
|
||||
display->send(display->bus, false, ((uint8_t*)buffer),
|
||||
display->core.send(display->core.bus, false, false, ((uint8_t*)buffer),
|
||||
buffer_size * 2);
|
||||
index = 0;
|
||||
}
|
||||
|
@ -67,6 +67,6 @@ void render_stage(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1,
|
|||
}
|
||||
// Send the remaining data.
|
||||
if (index) {
|
||||
display->send(display->bus, false, ((uint8_t*)buffer), index * 2);
|
||||
display->core.send(display->core.bus, false, false, ((uint8_t*)buffer), index * 2);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -33,7 +33,9 @@
|
|||
#include "shared-bindings/microcontroller/Pin.h"
|
||||
#include "shared-bindings/time/__init__.h"
|
||||
#include "shared-module/displayio/__init__.h"
|
||||
#include "shared-module/displayio/display_core.h"
|
||||
#include "supervisor/shared/display.h"
|
||||
#include "supervisor/usb.h"
|
||||
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
@ -41,48 +43,34 @@
|
|||
#include "tick.h"
|
||||
|
||||
void common_hal_displayio_display_construct(displayio_display_obj_t* self,
|
||||
mp_obj_t bus, uint16_t width, uint16_t height, int16_t colstart, int16_t rowstart, uint16_t rotation,
|
||||
uint16_t color_depth, bool grayscale, 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,
|
||||
bool single_byte_bounds, bool data_as_commands) {
|
||||
self->colorspace.depth = color_depth;
|
||||
self->colorspace.grayscale = grayscale;
|
||||
self->colorspace.pixels_in_byte_share_row = pixels_in_byte_share_row;
|
||||
self->colorspace.bytes_per_cell = bytes_per_cell;
|
||||
self->colorspace.reverse_pixels_in_byte = reverse_pixels_in_byte;
|
||||
mp_obj_t bus, uint16_t width, uint16_t height, int16_t colstart, int16_t rowstart,
|
||||
uint16_t rotation, uint16_t color_depth, bool grayscale, 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,
|
||||
bool single_byte_bounds, bool data_as_commands, bool auto_refresh, uint16_t native_frames_per_second) {
|
||||
uint16_t ram_width = 0x100;
|
||||
uint16_t ram_height = 0x100;
|
||||
if (single_byte_bounds) {
|
||||
ram_width = 0xff;
|
||||
ram_height = 0xff;
|
||||
}
|
||||
displayio_display_core_construct(&self->core, bus, width, height, ram_width, ram_height, colstart, rowstart, rotation,
|
||||
color_depth, grayscale, pixels_in_byte_share_row, bytes_per_cell, reverse_pixels_in_byte);
|
||||
|
||||
self->set_column_command = set_column_command;
|
||||
self->set_row_command = set_row_command;
|
||||
self->write_ram_command = write_ram_command;
|
||||
self->refresh = false;
|
||||
self->current_group = NULL;
|
||||
self->colstart = colstart;
|
||||
self->rowstart = rowstart;
|
||||
self->brightness_command = brightness_command;
|
||||
self->auto_brightness = auto_brightness;
|
||||
self->data_as_commands = data_as_commands;
|
||||
self->single_byte_bounds = single_byte_bounds;
|
||||
|
||||
if (MP_OBJ_IS_TYPE(bus, &displayio_parallelbus_type)) {
|
||||
self->begin_transaction = common_hal_displayio_parallelbus_begin_transaction;
|
||||
self->send = common_hal_displayio_parallelbus_send;
|
||||
self->end_transaction = common_hal_displayio_parallelbus_end_transaction;
|
||||
} else if (MP_OBJ_IS_TYPE(bus, &displayio_fourwire_type)) {
|
||||
self->begin_transaction = common_hal_displayio_fourwire_begin_transaction;
|
||||
self->send = common_hal_displayio_fourwire_send;
|
||||
self->end_transaction = common_hal_displayio_fourwire_end_transaction;
|
||||
} else if (MP_OBJ_IS_TYPE(bus, &displayio_i2cdisplay_type)) {
|
||||
self->begin_transaction = common_hal_displayio_i2cdisplay_begin_transaction;
|
||||
self->send = common_hal_displayio_i2cdisplay_send;
|
||||
self->end_transaction = common_hal_displayio_i2cdisplay_end_transaction;
|
||||
} else {
|
||||
mp_raise_ValueError(translate("Unsupported display bus type"));
|
||||
}
|
||||
self->bus = bus;
|
||||
self->native_frames_per_second = native_frames_per_second;
|
||||
self->native_frame_time = 1000 / native_frames_per_second;
|
||||
|
||||
uint32_t i = 0;
|
||||
while (!self->begin_transaction(self->bus)) {
|
||||
while (!displayio_display_core_begin_transaction(&self->core)) {
|
||||
RUN_BACKGROUND_TASKS;
|
||||
}
|
||||
while (i < init_sequence_len) {
|
||||
|
@ -95,10 +83,10 @@ void common_hal_displayio_display_construct(displayio_display_obj_t* self,
|
|||
uint8_t full_command[data_size + 1];
|
||||
full_command[0] = cmd[0];
|
||||
memcpy(full_command + 1, data, data_size);
|
||||
self->send(self->bus, true, true, full_command, data_size + 1);
|
||||
self->core.send(self->core.bus, true, true, full_command, data_size + 1);
|
||||
} else {
|
||||
self->send(self->bus, true, true, cmd, 1);
|
||||
self->send(self->bus, false, false, data, data_size);
|
||||
self->core.send(self->core.bus, true, true, cmd, 1);
|
||||
self->core.send(self->core.bus, false, false, data, data_size);
|
||||
}
|
||||
uint16_t delay_length_ms = 10;
|
||||
if (delay) {
|
||||
|
@ -111,33 +99,10 @@ void common_hal_displayio_display_construct(displayio_display_obj_t* self,
|
|||
common_hal_time_delay_ms(delay_length_ms);
|
||||
i += 2 + data_size;
|
||||
}
|
||||
self->end_transaction(self->bus);
|
||||
self->core.end_transaction(self->core.bus);
|
||||
|
||||
supervisor_start_terminal(width, height);
|
||||
|
||||
self->width = width;
|
||||
self->height = height;
|
||||
self->rotation = rotation % 360;
|
||||
self->transform.x = 0;
|
||||
self->transform.y = 0;
|
||||
self->transform.scale = 1;
|
||||
self->transform.mirror_x = false;
|
||||
self->transform.mirror_y = false;
|
||||
self->transform.transpose_xy = false;
|
||||
if (rotation == 0 || rotation == 180) {
|
||||
if (rotation == 180) {
|
||||
self->transform.mirror_x = true;
|
||||
self->transform.mirror_y = true;
|
||||
}
|
||||
} else {
|
||||
self->transform.transpose_xy = true;
|
||||
if (rotation == 270) {
|
||||
self->transform.mirror_y = true;
|
||||
} else {
|
||||
self->transform.mirror_x = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Always set the backlight type in case we're reusing memory.
|
||||
self->backlight_inout.base.type = &mp_type_NoneType;
|
||||
if (backlight_pin != NULL && common_hal_mcu_pin_is_free(backlight_pin)) {
|
||||
|
@ -158,101 +123,98 @@ void common_hal_displayio_display_construct(displayio_display_obj_t* self,
|
|||
self->current_brightness = -1.0;
|
||||
}
|
||||
|
||||
self->area.x1 = 0;
|
||||
self->area.y1 = 0;
|
||||
self->area.next = NULL;
|
||||
|
||||
self->transform.dx = 1;
|
||||
self->transform.dy = 1;
|
||||
if (self->transform.transpose_xy) {
|
||||
self->area.x2 = height;
|
||||
self->area.y2 = width;
|
||||
if (self->transform.mirror_x) {
|
||||
self->transform.x = height;
|
||||
self->transform.dx = -1;
|
||||
}
|
||||
if (self->transform.mirror_y) {
|
||||
self->transform.y = width;
|
||||
self->transform.dy = -1;
|
||||
}
|
||||
} else {
|
||||
self->area.x2 = width;
|
||||
self->area.y2 = height;
|
||||
if (self->transform.mirror_x) {
|
||||
self->transform.x = width;
|
||||
self->transform.dx = -1;
|
||||
}
|
||||
if (self->transform.mirror_y) {
|
||||
self->transform.y = height;
|
||||
self->transform.dy = -1;
|
||||
}
|
||||
}
|
||||
|
||||
// Set the group after initialization otherwise we may send pixels while we delay in
|
||||
// initialization.
|
||||
common_hal_displayio_display_show(self, &circuitpython_splash);
|
||||
}
|
||||
|
||||
bool common_hal_displayio_display_show(displayio_display_obj_t* self, displayio_group_t* root_group) {
|
||||
if (root_group == NULL) {
|
||||
if (!circuitpython_splash.in_group) {
|
||||
root_group = &circuitpython_splash;
|
||||
} else if (self->current_group == &circuitpython_splash) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (root_group == self->current_group) {
|
||||
return true;
|
||||
}
|
||||
if (root_group != NULL && root_group->in_group) {
|
||||
return false;
|
||||
}
|
||||
if (self->current_group != NULL) {
|
||||
self->current_group->in_group = false;
|
||||
}
|
||||
|
||||
if (root_group != NULL) {
|
||||
displayio_group_update_transform(root_group, &self->transform);
|
||||
root_group->in_group = true;
|
||||
}
|
||||
self->current_group = root_group;
|
||||
self->full_refresh = true;
|
||||
common_hal_displayio_display_refresh_soon(self);
|
||||
return true;
|
||||
return displayio_display_core_show(&self->core, root_group);
|
||||
}
|
||||
|
||||
const displayio_area_t* displayio_display_get_refresh_areas(displayio_display_obj_t *self) {
|
||||
if (self->full_refresh) {
|
||||
self->area.next = NULL;
|
||||
return &self->area;
|
||||
uint16_t common_hal_displayio_display_get_width(displayio_display_obj_t* self){
|
||||
return displayio_display_core_get_width(&self->core);
|
||||
}
|
||||
|
||||
uint16_t common_hal_displayio_display_get_height(displayio_display_obj_t* self){
|
||||
return displayio_display_core_get_height(&self->core);
|
||||
}
|
||||
|
||||
bool common_hal_displayio_display_get_auto_brightness(displayio_display_obj_t* self) {
|
||||
return self->auto_brightness;
|
||||
}
|
||||
|
||||
void common_hal_displayio_display_set_auto_brightness(displayio_display_obj_t* self, bool auto_brightness) {
|
||||
self->auto_brightness = auto_brightness;
|
||||
}
|
||||
|
||||
mp_float_t common_hal_displayio_display_get_brightness(displayio_display_obj_t* self) {
|
||||
return self->current_brightness;
|
||||
}
|
||||
|
||||
bool common_hal_displayio_display_set_brightness(displayio_display_obj_t* self, mp_float_t brightness) {
|
||||
self->updating_backlight = true;
|
||||
bool ok = false;
|
||||
if (self->backlight_pwm.base.type == &pulseio_pwmout_type) {
|
||||
common_hal_pulseio_pwmout_set_duty_cycle(&self->backlight_pwm, (uint16_t) (0xffff * brightness));
|
||||
ok = true;
|
||||
} else if (self->backlight_inout.base.type == &digitalio_digitalinout_type) {
|
||||
common_hal_digitalio_digitalinout_set_value(&self->backlight_inout, brightness > 0.99);
|
||||
ok = true;
|
||||
} else if (self->brightness_command != NO_BRIGHTNESS_COMMAND) {
|
||||
ok = displayio_display_core_begin_transaction(&self->core);
|
||||
if (ok) {
|
||||
if (self->data_as_commands) {
|
||||
uint8_t set_brightness[2] = {self->brightness_command, (uint8_t) (0xff * brightness)};
|
||||
self->core.send(self->core.bus, true, true, set_brightness, 2);
|
||||
} else {
|
||||
uint8_t command = self->brightness_command;
|
||||
uint8_t hex_brightness = 0xff * brightness;
|
||||
self->core.send(self->core.bus, true, true, &command, 1);
|
||||
self->core.send(self->core.bus, false, false, &hex_brightness, 1);
|
||||
}
|
||||
displayio_display_core_end_transaction(&self->core);
|
||||
}
|
||||
|
||||
}
|
||||
self->updating_backlight = false;
|
||||
if (ok) {
|
||||
self->current_brightness = brightness;
|
||||
}
|
||||
return ok;
|
||||
}
|
||||
|
||||
mp_obj_t common_hal_displayio_display_get_bus(displayio_display_obj_t* self) {
|
||||
return self->core.bus;
|
||||
}
|
||||
|
||||
STATIC const displayio_area_t* _get_refresh_areas(displayio_display_obj_t *self) {
|
||||
if (self->core.full_refresh) {
|
||||
self->core.area.next = NULL;
|
||||
return &self->core.area;
|
||||
} else {
|
||||
if (self->current_group == NULL || self->current_group->base.type != &displayio_group_type) {
|
||||
asm("bkpt");
|
||||
}
|
||||
return displayio_group_get_refresh_areas(self->current_group, NULL);
|
||||
return displayio_group_get_refresh_areas(self->core.current_group, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
int32_t common_hal_displayio_display_wait_for_frame(displayio_display_obj_t* self) {
|
||||
uint64_t last_refresh = self->last_refresh;
|
||||
// Don't try to refresh if we got an exception.
|
||||
while (last_refresh == self->last_refresh && MP_STATE_VM(mp_pending_exception) == NULL) {
|
||||
RUN_BACKGROUND_TASKS;
|
||||
STATIC void _send_pixels(displayio_display_obj_t* self, uint8_t* pixels, uint32_t length) {
|
||||
if (!self->data_as_commands) {
|
||||
self->core.send(self->core.bus, true, true, &self->write_ram_command, 1);
|
||||
}
|
||||
return 0;
|
||||
self->core.send(self->core.bus, false, false, pixels, length);
|
||||
}
|
||||
|
||||
STATIC bool refresh_area(displayio_display_obj_t* display, const displayio_area_t* area) {
|
||||
STATIC bool _refresh_area(displayio_display_obj_t* self, 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)) {
|
||||
if (!displayio_display_core_clip_area(&self->core, 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;
|
||||
uint8_t pixels_per_word = (sizeof(uint32_t) * 8) / self->core.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);
|
||||
|
@ -260,8 +222,8 @@ STATIC bool refresh_area(displayio_display_obj_t* display, const displayio_area_
|
|||
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 (self->core.colorspace.depth < 8 && !self->core.colorspace.pixels_in_byte_share_row) {
|
||||
uint8_t pixels_per_byte = 8 / self->core.colorspace.depth;
|
||||
if (rows_per_buffer % pixels_per_byte != 0) {
|
||||
rows_per_buffer -= rows_per_buffer % pixels_per_byte;
|
||||
}
|
||||
|
@ -296,15 +258,13 @@ STATIC bool refresh_area(displayio_display_obj_t* display, const displayio_area_
|
|||
}
|
||||
remaining_rows -= rows_per_buffer;
|
||||
|
||||
displayio_display_begin_transaction(display);
|
||||
displayio_display_set_region_to_update(display, &subrectangle);
|
||||
displayio_display_end_transaction(display);
|
||||
displayio_display_core_set_region_to_update(&self->core, self->set_column_command, self->set_row_command, NO_COMMAND, NO_COMMAND, self->data_as_commands, false, &subrectangle);
|
||||
|
||||
uint16_t subrectangle_size_bytes;
|
||||
if (display->colorspace.depth >= 8) {
|
||||
subrectangle_size_bytes = displayio_area_size(&subrectangle) * (display->colorspace.depth / 8);
|
||||
if (self->core.colorspace.depth >= 8) {
|
||||
subrectangle_size_bytes = displayio_area_size(&subrectangle) * (self->core.colorspace.depth / 8);
|
||||
} else {
|
||||
subrectangle_size_bytes = displayio_area_size(&subrectangle) / (8 / display->colorspace.depth);
|
||||
subrectangle_size_bytes = displayio_area_size(&subrectangle) / (8 / self->core.colorspace.depth);
|
||||
}
|
||||
|
||||
for (uint16_t k = 0; k < mask_length; k++) {
|
||||
|
@ -314,14 +274,16 @@ STATIC bool refresh_area(displayio_display_obj_t* display, const displayio_area_
|
|||
buffer[k] = 0x00000000;
|
||||
}
|
||||
|
||||
displayio_display_fill_area(display, &subrectangle, mask, buffer);
|
||||
displayio_display_core_fill_area(&self->core, &subrectangle, mask, buffer);
|
||||
|
||||
if (!displayio_display_begin_transaction(display)) {
|
||||
// Can't acquire display bus; skip the rest of the data. Try next display.
|
||||
// Can't acquire display bus; skip the rest of the data.
|
||||
if (!displayio_display_core_bus_free(&self->core)) {
|
||||
return false;
|
||||
}
|
||||
displayio_display_send_pixels(display, (uint8_t*) buffer, subrectangle_size_bytes);
|
||||
displayio_display_end_transaction(display);
|
||||
|
||||
displayio_display_core_begin_transaction(&self->core);
|
||||
_send_pixels(self, (uint8_t*) buffer, subrectangle_size_bytes);
|
||||
displayio_display_core_end_transaction(&self->core);
|
||||
|
||||
// TODO(tannewt): Make refresh displays faster so we don't starve other
|
||||
// background tasks.
|
||||
|
@ -330,182 +292,59 @@ STATIC bool refresh_area(displayio_display_obj_t* display, const displayio_area_
|
|||
return true;
|
||||
}
|
||||
|
||||
STATIC void refresh_display(displayio_display_obj_t* self) {
|
||||
if (!displayio_display_begin_transaction(self)) {
|
||||
STATIC void _refresh_display(displayio_display_obj_t* self) {
|
||||
if (!displayio_display_core_bus_free(&self->core)) {
|
||||
// Can't acquire display bus; skip updating this display. Try next display.
|
||||
continue;
|
||||
return;
|
||||
}
|
||||
displayio_display_end_transaction(self);
|
||||
displayio_display_start_refresh(self);
|
||||
const displayio_area_t* current_area = displayio_display_get_refresh_areas(self);
|
||||
displayio_display_core_start_refresh(&self->core);
|
||||
const displayio_area_t* current_area = _get_refresh_areas(self);
|
||||
while (current_area != NULL) {
|
||||
refresh_area(self, current_area);
|
||||
_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;
|
||||
}
|
||||
|
||||
uint16_t common_hal_displayio_display_get_width(displayio_display_obj_t* self){
|
||||
return self->width;
|
||||
}
|
||||
|
||||
uint16_t common_hal_displayio_display_get_height(displayio_display_obj_t* self){
|
||||
return self->height;
|
||||
displayio_display_core_finish_refresh(&self->core);
|
||||
}
|
||||
|
||||
uint16_t common_hal_displayio_display_get_rotation(displayio_display_obj_t* self){
|
||||
return self->rotation;
|
||||
return self->core.rotation;
|
||||
}
|
||||
|
||||
void common_hal_displayio_display_set_auto_brightness(displayio_display_obj_t* self, bool auto_brightness) {
|
||||
self->auto_brightness = auto_brightness;
|
||||
}
|
||||
|
||||
mp_float_t common_hal_displayio_display_get_brightness(displayio_display_obj_t* self) {
|
||||
return self->current_brightness;
|
||||
}
|
||||
|
||||
bool common_hal_displayio_display_set_brightness(displayio_display_obj_t* self, mp_float_t brightness) {
|
||||
self->updating_backlight = true;
|
||||
bool ok = false;
|
||||
if (self->backlight_pwm.base.type == &pulseio_pwmout_type) {
|
||||
common_hal_pulseio_pwmout_set_duty_cycle(&self->backlight_pwm, (uint16_t) (0xffff * brightness));
|
||||
ok = true;
|
||||
} else if (self->backlight_inout.base.type == &digitalio_digitalinout_type) {
|
||||
common_hal_digitalio_digitalinout_set_value(&self->backlight_inout, brightness > 0.99);
|
||||
ok = true;
|
||||
} else if (self->brightness_command != NO_BRIGHTNESS_COMMAND) {
|
||||
ok = self->begin_transaction(self->bus);
|
||||
if (ok) {
|
||||
if (self->data_as_commands) {
|
||||
uint8_t set_brightness[2] = {self->brightness_command, (uint8_t) (0xff * brightness)};
|
||||
self->send(self->bus, true, true, set_brightness, 2);
|
||||
} else {
|
||||
uint8_t command = self->brightness_command;
|
||||
uint8_t hex_brightness = 0xff * brightness;
|
||||
self->send(self->bus, true, true, &command, 1);
|
||||
self->send(self->bus, false, false, &hex_brightness, 1);
|
||||
}
|
||||
self->end_transaction(self->bus);
|
||||
void common_hal_displayio_display_refresh(displayio_display_obj_t* self, uint32_t target_frame_time, uint32_t maximum_frame_time) {
|
||||
if (!self->auto_refresh) {
|
||||
uint64_t current_time = ticks_ms;
|
||||
uint32_t current_frame_time = current_time - self->core.last_refresh;
|
||||
// Test to see if the real frame time is below our minimum.
|
||||
if (current_frame_time > maximum_frame_time) {
|
||||
mp_raise_RuntimeError(translate("Below minimum frame rate"));
|
||||
}
|
||||
|
||||
}
|
||||
self->updating_backlight = false;
|
||||
if (ok) {
|
||||
self->current_brightness = brightness;
|
||||
}
|
||||
return ok;
|
||||
}
|
||||
|
||||
bool displayio_display_begin_transaction(displayio_display_obj_t* self) {
|
||||
return self->begin_transaction(self->bus);
|
||||
}
|
||||
|
||||
void displayio_display_end_transaction(displayio_display_obj_t* self) {
|
||||
self->end_transaction(self->bus);
|
||||
}
|
||||
|
||||
void displayio_display_set_region_to_update(displayio_display_obj_t* self, displayio_area_t* area) {
|
||||
uint16_t x1 = area->x1;
|
||||
uint16_t x2 = area->x2;
|
||||
uint16_t y1 = area->y1;
|
||||
uint16_t y2 = area->y2;
|
||||
// Collapse down the dimension where multiple pixels are in a byte.
|
||||
if (self->colorspace.depth < 8) {
|
||||
uint8_t pixels_per_byte = 8 / self->colorspace.depth;
|
||||
if (self->colorspace.pixels_in_byte_share_row) {
|
||||
x1 /= pixels_per_byte * self->colorspace.bytes_per_cell;
|
||||
x2 /= pixels_per_byte * self->colorspace.bytes_per_cell;
|
||||
} else {
|
||||
y1 /= pixels_per_byte * self->colorspace.bytes_per_cell;
|
||||
y2 /= pixels_per_byte * self->colorspace.bytes_per_cell;
|
||||
uint32_t current_call_time = current_time - self->last_refresh_call;
|
||||
self->last_refresh_call = current_time;
|
||||
// Skip the actual refresh to help catch up.
|
||||
if (current_call_time > target_frame_time) {
|
||||
return;
|
||||
}
|
||||
uint32_t remaining_time = target_frame_time - (current_frame_time % target_frame_time);
|
||||
// We're ahead of the game so wait until we align with the frame rate.
|
||||
while (ticks_ms - self->last_refresh_call < remaining_time) {
|
||||
#ifdef MICROPY_VM_HOOK_LOOP
|
||||
MICROPY_VM_HOOK_LOOP ;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
// Set column.
|
||||
uint8_t data[5];
|
||||
data[0] = self->set_column_command;
|
||||
uint8_t data_length = 1;
|
||||
if (!self->data_as_commands) {
|
||||
self->send(self->bus, true, true, data, 1);
|
||||
data_length = 0;
|
||||
}
|
||||
if (self->single_byte_bounds) {
|
||||
data[data_length++] = x1 + self->colstart;
|
||||
data[data_length++] = x2 - 1 + self->colstart;
|
||||
} else {
|
||||
x1 += self->colstart;
|
||||
x2 += self->colstart - 1;
|
||||
data[data_length++] = x1 >> 8;
|
||||
data[data_length++] = x1 & 0xff;
|
||||
data[data_length++] = x2 >> 8;
|
||||
data[data_length++] = x2 & 0xff;
|
||||
}
|
||||
self->send(self->bus, self->data_as_commands, self->data_as_commands, data, data_length);
|
||||
|
||||
// Set row.
|
||||
data[0] = self->set_row_command;
|
||||
data_length = 1;
|
||||
if (!self->data_as_commands) {
|
||||
self->send(self->bus, true, true, data, 1);
|
||||
data_length = 0;
|
||||
}
|
||||
if (self->single_byte_bounds) {
|
||||
data[data_length++] = y1 + self->rowstart;
|
||||
data[data_length++] = y2 - 1 + self->rowstart;
|
||||
} else {
|
||||
y1 += self->rowstart;
|
||||
y2 += self->rowstart - 1;
|
||||
data[data_length++] = y1 >> 8;
|
||||
data[data_length++] = y1 & 0xff;
|
||||
data[data_length++] = y2 >> 8;
|
||||
data[data_length++] = y2 & 0xff;
|
||||
}
|
||||
self->send(self->bus, self->data_as_commands, self->data_as_commands, data, data_length);
|
||||
_refresh_display(self);
|
||||
}
|
||||
|
||||
void displayio_display_start_refresh(displayio_display_obj_t* self) {
|
||||
self->last_refresh = ticks_ms;
|
||||
bool common_hal_displayio_display_get_auto_refresh(displayio_display_obj_t* self) {
|
||||
return self->auto_refresh;
|
||||
}
|
||||
|
||||
bool displayio_display_frame_queued(displayio_display_obj_t* self) {
|
||||
if (self->current_group == NULL) {
|
||||
return false;
|
||||
}
|
||||
// Refresh at ~60 fps.
|
||||
return (ticks_ms - self->last_refresh) > 16;
|
||||
void common_hal_displayio_display_set_auto_refresh(displayio_display_obj_t* self,
|
||||
bool auto_refresh) {
|
||||
self->auto_refresh = auto_refresh;
|
||||
}
|
||||
|
||||
void displayio_display_finish_refresh(displayio_display_obj_t* self) {
|
||||
if (self->current_group != NULL) {
|
||||
displayio_group_finish_refresh(self->current_group);
|
||||
}
|
||||
self->refresh = false;
|
||||
self->full_refresh = false;
|
||||
self->last_refresh = ticks_ms;
|
||||
}
|
||||
|
||||
void displayio_display_send_pixels(displayio_display_obj_t* self, uint8_t* pixels, uint32_t length) {
|
||||
if (!self->data_as_commands) {
|
||||
self->send(self->bus, true, true, &self->write_ram_command, 1);
|
||||
}
|
||||
self->send(self->bus, false, false, pixels, length);
|
||||
}
|
||||
|
||||
void displayio_display_update_backlight(displayio_display_obj_t* self) {
|
||||
STATIC void _update_backlight(displayio_display_obj_t* self) {
|
||||
if (!self->auto_brightness || self->updating_backlight) {
|
||||
return;
|
||||
}
|
||||
|
@ -520,17 +359,15 @@ void displayio_display_update_backlight(displayio_display_obj_t* self) {
|
|||
}
|
||||
|
||||
void displayio_display_background(displayio_display_obj_t* self) {
|
||||
displayio_display_update_backlight(self);
|
||||
_update_backlight(self);
|
||||
|
||||
if (self->auto_refresh && (ticks_ms - self->last_refresh) > 16) {
|
||||
display_refresh(self);
|
||||
if (self->auto_refresh && (ticks_ms - self->core.last_refresh) > self->native_frame_time) {
|
||||
_refresh_display(self);
|
||||
}
|
||||
}
|
||||
|
||||
void release_display(displayio_display_obj_t* self) {
|
||||
if (self->current_group != NULL) {
|
||||
self->current_group->in_group = false;
|
||||
}
|
||||
release_display_core(&self->core);
|
||||
if (self->backlight_pwm.base.type == &pulseio_pwmout_type) {
|
||||
common_hal_pulseio_pwmout_reset_ok(&self->backlight_pwm);
|
||||
common_hal_pulseio_pwmout_deinit(&self->backlight_pwm);
|
||||
|
@ -539,34 +376,6 @@ void release_display(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) {
|
||||
return displayio_group_fill_area(self->current_group, &self->colorspace, area, mask, buffer);
|
||||
}
|
||||
|
||||
bool displayio_display_clip_area(displayio_display_obj_t *self, const displayio_area_t* area, displayio_area_t* clipped) {
|
||||
bool overlaps = displayio_area_compute_overlap(&self->area, area, clipped);
|
||||
if (!overlaps) {
|
||||
return false;
|
||||
}
|
||||
// Expand the area if we have multiple pixels per byte and we need to byte
|
||||
// align the bounds.
|
||||
if (self->colorspace.depth < 8) {
|
||||
uint8_t pixels_per_byte = 8 / self->colorspace.depth * self->colorspace.bytes_per_cell;
|
||||
if (self->colorspace.pixels_in_byte_share_row) {
|
||||
if (clipped->x1 % pixels_per_byte != 0) {
|
||||
clipped->x1 -= clipped->x1 % pixels_per_byte;
|
||||
}
|
||||
if (clipped->x2 % pixels_per_byte != 0) {
|
||||
clipped->x2 += pixels_per_byte - clipped->x2 % pixels_per_byte;
|
||||
}
|
||||
} else {
|
||||
if (clipped->y1 % pixels_per_byte != 0) {
|
||||
clipped->y1 -= clipped->y1 % pixels_per_byte;
|
||||
}
|
||||
if (clipped->y2 % pixels_per_byte != 0) {
|
||||
clipped->y2 += pixels_per_byte - clipped->y2 % pixels_per_byte;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
void displayio_display_collect_ptrs(displayio_display_obj_t* self) {
|
||||
displayio_display_core_collect_ptrs(&self->core);
|
||||
}
|
||||
|
|
|
@ -32,46 +32,33 @@
|
|||
#include "shared-bindings/pulseio/PWMOut.h"
|
||||
|
||||
#include "shared-module/displayio/area.h"
|
||||
|
||||
typedef bool (*display_bus_begin_transaction)(mp_obj_t bus);
|
||||
typedef void (*display_bus_send)(mp_obj_t bus, bool command, bool toggle_every_byte, uint8_t *data, uint32_t data_length);
|
||||
typedef void (*display_bus_end_transaction)(mp_obj_t bus);
|
||||
#include "shared-module/displayio/display_core.h"
|
||||
|
||||
typedef struct {
|
||||
mp_obj_base_t base;
|
||||
mp_obj_t bus;
|
||||
displayio_group_t *current_group;
|
||||
uint64_t last_refresh;
|
||||
display_bus_begin_transaction begin_transaction;
|
||||
display_bus_send send;
|
||||
display_bus_end_transaction end_transaction;
|
||||
displayio_display_core_t core;
|
||||
union {
|
||||
digitalio_digitalinout_obj_t backlight_inout;
|
||||
pulseio_pwmout_obj_t backlight_pwm;
|
||||
};
|
||||
uint64_t last_backlight_refresh;
|
||||
displayio_buffer_transform_t transform;
|
||||
displayio_area_t area;
|
||||
uint64_t last_refresh_call;
|
||||
mp_float_t current_brightness;
|
||||
uint16_t width;
|
||||
uint16_t height;
|
||||
uint16_t rotation;
|
||||
_displayio_colorspace_t colorspace;
|
||||
int16_t colstart;
|
||||
int16_t rowstart;
|
||||
uint16_t brightness_command;
|
||||
uint16_t native_frames_per_second;
|
||||
uint16_t native_frame_time;
|
||||
uint8_t set_column_command;
|
||||
uint8_t set_row_command;
|
||||
uint8_t write_ram_command;
|
||||
bool auto_refresh;
|
||||
bool single_byte_bounds;
|
||||
bool data_as_commands;
|
||||
bool auto_brightness;
|
||||
bool updating_backlight;
|
||||
bool full_refresh; // New group means we need to refresh the whole display.
|
||||
} displayio_display_obj_t;
|
||||
|
||||
void displayio_display_background(displayio_display_obj_t* self);
|
||||
void release_display(displayio_display_obj_t* self);
|
||||
|
||||
void displayio_display_collect_ptrs(displayio_display_obj_t* self);
|
||||
|
||||
#endif // MICROPY_INCLUDED_SHARED_MODULE_DISPLAYIO_DISPLAY_H
|
||||
|
|
|
@ -50,18 +50,14 @@ void common_hal_displayio_epaperdisplay_construct(displayio_epaperdisplay_obj_t*
|
|||
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 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;
|
||||
self->colorspace.pixels_in_byte_share_row = true;
|
||||
self->colorspace.bytes_per_cell = 1;
|
||||
self->colorspace.reverse_pixels_in_byte = true;
|
||||
|
||||
if (highlight_color != 0x000000) {
|
||||
self->colorspace.tricolor = true;
|
||||
self->colorspace.tricolor_hue = displayio_colorconverter_compute_hue(highlight_color);
|
||||
self->colorspace.tricolor_luma = displayio_colorconverter_compute_luma(highlight_color);
|
||||
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);
|
||||
}
|
||||
|
||||
displayio_display_core_construct(&self->core, bus, width, height, ram_width, ram_height, colstart, rowstart, rotation, 1, true, true, 1, true);
|
||||
|
||||
self->set_column_window_command = set_column_window_command;
|
||||
self->set_row_window_command = set_row_window_command;
|
||||
self->set_current_column_command = set_current_column_command;
|
||||
|
@ -72,10 +68,6 @@ void common_hal_displayio_epaperdisplay_construct(displayio_epaperdisplay_obj_t*
|
|||
self->color_bits_inverted = color_bits_inverted;
|
||||
self->refresh_display_command = refresh_display_command;
|
||||
self->busy_state = busy_state;
|
||||
self->refresh = true;
|
||||
self->current_group = NULL;
|
||||
self->colstart = colstart;
|
||||
self->rowstart = rowstart;
|
||||
self->refreshing = false;
|
||||
self->milliseconds_per_frame = seconds_per_frame * 1000;
|
||||
self->always_toggle_chip_select = always_toggle_chip_select;
|
||||
|
@ -85,88 +77,6 @@ void common_hal_displayio_epaperdisplay_construct(displayio_epaperdisplay_obj_t*
|
|||
self->stop_sequence = stop_sequence;
|
||||
self->stop_sequence_len = stop_sequence_len;
|
||||
|
||||
|
||||
if (MP_OBJ_IS_TYPE(bus, &displayio_parallelbus_type)) {
|
||||
self->bus_reset = common_hal_displayio_parallelbus_reset;
|
||||
self->bus_free = common_hal_displayio_parallelbus_bus_free;
|
||||
self->begin_transaction = common_hal_displayio_parallelbus_begin_transaction;
|
||||
self->send = common_hal_displayio_parallelbus_send;
|
||||
self->end_transaction = common_hal_displayio_parallelbus_end_transaction;
|
||||
} else if (MP_OBJ_IS_TYPE(bus, &displayio_fourwire_type)) {
|
||||
self->bus_reset = common_hal_displayio_fourwire_reset;
|
||||
self->bus_free = common_hal_displayio_fourwire_bus_free;
|
||||
self->begin_transaction = common_hal_displayio_fourwire_begin_transaction;
|
||||
self->send = common_hal_displayio_fourwire_send;
|
||||
self->end_transaction = common_hal_displayio_fourwire_end_transaction;
|
||||
} else if (MP_OBJ_IS_TYPE(bus, &displayio_i2cdisplay_type)) {
|
||||
self->bus_reset = common_hal_displayio_i2cdisplay_reset;
|
||||
self->bus_free = common_hal_displayio_i2cdisplay_bus_free;
|
||||
self->begin_transaction = common_hal_displayio_i2cdisplay_begin_transaction;
|
||||
self->send = common_hal_displayio_i2cdisplay_send;
|
||||
self->end_transaction = common_hal_displayio_i2cdisplay_end_transaction;
|
||||
} else {
|
||||
mp_raise_ValueError(translate("Unsupported display bus type"));
|
||||
}
|
||||
self->bus = bus;
|
||||
|
||||
supervisor_start_terminal(width, height);
|
||||
|
||||
self->width = width;
|
||||
self->height = height;
|
||||
rotation = rotation % 360;
|
||||
self->transform.x = 0;
|
||||
self->transform.y = 0;
|
||||
self->transform.scale = 1;
|
||||
self->transform.mirror_x = false;
|
||||
self->transform.mirror_y = false;
|
||||
self->transform.transpose_xy = false;
|
||||
if (rotation == 0 || rotation == 180) {
|
||||
if (rotation == 180) {
|
||||
self->transform.mirror_x = true;
|
||||
self->transform.mirror_y = true;
|
||||
}
|
||||
} else {
|
||||
self->transform.transpose_xy = true;
|
||||
if (rotation == 270) {
|
||||
self->transform.mirror_y = true;
|
||||
} else {
|
||||
self->transform.mirror_x = true;
|
||||
}
|
||||
}
|
||||
|
||||
self->ram_width = ram_width;
|
||||
self->ram_height = ram_height;
|
||||
|
||||
self->area.x1 = 0;
|
||||
self->area.y1 = 0;
|
||||
self->area.next = NULL;
|
||||
|
||||
self->transform.dx = 1;
|
||||
self->transform.dy = 1;
|
||||
if (self->transform.transpose_xy) {
|
||||
self->area.x2 = height;
|
||||
self->area.y2 = width;
|
||||
if (self->transform.mirror_x) {
|
||||
self->transform.x = height;
|
||||
self->transform.dx = -1;
|
||||
}
|
||||
if (self->transform.mirror_y) {
|
||||
self->transform.y = width;
|
||||
self->transform.dy = -1;
|
||||
}
|
||||
} else {
|
||||
self->area.x2 = width;
|
||||
self->area.y2 = height;
|
||||
if (self->transform.mirror_x) {
|
||||
self->transform.x = width;
|
||||
self->transform.dx = -1;
|
||||
}
|
||||
if (self->transform.mirror_y) {
|
||||
self->transform.y = height;
|
||||
self->transform.dy = -1;
|
||||
}
|
||||
}
|
||||
|
||||
self->busy.base.type = &mp_type_NoneType;
|
||||
if (busy_pin != NULL) {
|
||||
self->busy.base.type = &digitalio_digitalinout_type;
|
||||
|
@ -179,77 +89,37 @@ void common_hal_displayio_epaperdisplay_construct(displayio_epaperdisplay_obj_t*
|
|||
// TODO: Clear
|
||||
}
|
||||
|
||||
supervisor_start_terminal(width, height);
|
||||
|
||||
// Set the group after initialization otherwise we may send pixels while we delay in
|
||||
// initialization.
|
||||
common_hal_displayio_epaperdisplay_show(self, &circuitpython_splash);
|
||||
}
|
||||
|
||||
bool common_hal_displayio_epaperdisplay_show(displayio_epaperdisplay_obj_t* self, displayio_group_t* root_group) {
|
||||
if (root_group == NULL && !circuitpython_splash.in_group) {
|
||||
root_group = &circuitpython_splash;
|
||||
}
|
||||
if (root_group == self->current_group) {
|
||||
return true;
|
||||
}
|
||||
if (root_group != NULL && root_group->in_group) {
|
||||
return false;
|
||||
}
|
||||
if (self->current_group != NULL) {
|
||||
self->current_group->in_group = false;
|
||||
}
|
||||
if (root_group != NULL) {
|
||||
displayio_group_update_transform(root_group, &self->transform);
|
||||
root_group->in_group = true;
|
||||
self->current_group = root_group;
|
||||
}
|
||||
self->full_refresh = true;
|
||||
return true;
|
||||
return displayio_display_core_show(&self->core, root_group);
|
||||
}
|
||||
|
||||
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) {
|
||||
asm("bkpt");
|
||||
}
|
||||
if (self->full_refresh) {
|
||||
first_area = &self->area;
|
||||
if (self->core.full_refresh) {
|
||||
first_area = &self->core.area;
|
||||
} else {
|
||||
first_area = displayio_group_get_refresh_areas(self->current_group, NULL);
|
||||
first_area = displayio_group_get_refresh_areas(self->core.current_group, NULL);
|
||||
}
|
||||
if (first_area != NULL && self->set_row_window_command == NO_COMMAND) {
|
||||
self->area.next = NULL;
|
||||
return &self->area;
|
||||
self->core.area.next = NULL;
|
||||
return &self->core.area;
|
||||
}
|
||||
return first_area;
|
||||
}
|
||||
|
||||
int32_t common_hal_displayio_epaperdisplay_wait_for_frame(displayio_epaperdisplay_obj_t* self) {
|
||||
uint64_t last_refresh = self->last_refresh;
|
||||
// Don't try to refresh if we got an exception.
|
||||
while (last_refresh == self->last_refresh && MP_STATE_VM(mp_pending_exception) == NULL) {
|
||||
MICROPY_VM_HOOK_LOOP
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint16_t common_hal_displayio_epaperdisplay_get_width(displayio_epaperdisplay_obj_t* self){
|
||||
return self->width;
|
||||
return displayio_display_core_get_width(&self->core);
|
||||
}
|
||||
|
||||
uint16_t common_hal_displayio_epaperdisplay_get_height(displayio_epaperdisplay_obj_t* self){
|
||||
return self->height;
|
||||
}
|
||||
|
||||
bool displayio_epaperdisplay_bus_free(displayio_epaperdisplay_obj_t *self) {
|
||||
return self->bus_free(self->bus);
|
||||
}
|
||||
|
||||
bool displayio_epaperdisplay_begin_transaction(displayio_epaperdisplay_obj_t* self) {
|
||||
return self->begin_transaction(self->bus);
|
||||
}
|
||||
|
||||
void displayio_epaperdisplay_end_transaction(displayio_epaperdisplay_obj_t* self) {
|
||||
self->end_transaction(self->bus);
|
||||
return displayio_display_core_get_height(&self->core);
|
||||
}
|
||||
|
||||
STATIC void wait_for_busy(displayio_epaperdisplay_obj_t* self) {
|
||||
|
@ -271,10 +141,10 @@ STATIC void send_command_sequence(displayio_epaperdisplay_obj_t* self, bool shou
|
|||
bool delay = (data_size & DELAY) != 0;
|
||||
data_size &= ~DELAY;
|
||||
uint8_t *data = cmd + 2;
|
||||
self->begin_transaction(self->bus);
|
||||
self->send(self->bus, true, self->always_toggle_chip_select, cmd, 1);
|
||||
self->send(self->bus, false, self->always_toggle_chip_select, data, data_size);
|
||||
self->end_transaction(self->bus);
|
||||
displayio_display_core_begin_transaction(&self->core);
|
||||
self->core.send(self->core.bus, true, self->always_toggle_chip_select, cmd, 1);
|
||||
self->core.send(self->core.bus, false, self->always_toggle_chip_select, data, data_size);
|
||||
displayio_display_core_end_transaction(&self->core);
|
||||
uint16_t delay_length_ms = 0;
|
||||
if (delay) {
|
||||
data_size++;
|
||||
|
@ -291,87 +161,17 @@ STATIC void send_command_sequence(displayio_epaperdisplay_obj_t* self, bool shou
|
|||
}
|
||||
}
|
||||
|
||||
void displayio_epaperdisplay_set_region_to_update(displayio_epaperdisplay_obj_t* self, displayio_area_t* area) {
|
||||
if (self->set_row_window_command == NO_COMMAND) {
|
||||
return;
|
||||
}
|
||||
uint16_t x1 = area->x1;
|
||||
uint16_t x2 = area->x2;
|
||||
uint16_t y1 = area->y1;
|
||||
uint16_t y2 = area->y2;
|
||||
// Collapse down the dimension where multiple pixels are in a byte.
|
||||
uint8_t pixels_per_byte = 8 / self->colorspace.depth;
|
||||
x1 /= pixels_per_byte * self->colorspace.bytes_per_cell;
|
||||
x2 /= pixels_per_byte * self->colorspace.bytes_per_cell;
|
||||
|
||||
|
||||
// Set column.
|
||||
uint8_t data[5];
|
||||
data[0] = self->set_column_window_command;
|
||||
self->send(self->bus, true, self->always_toggle_chip_select, data, 1);
|
||||
uint8_t data_length = 0;
|
||||
if (self->ram_width / pixels_per_byte < 0x100) {
|
||||
data[data_length++] = x1 + self->colstart;
|
||||
data[data_length++] = x2 - 1 + self->colstart;
|
||||
} else {
|
||||
x1 += self->colstart;
|
||||
x2 += self->colstart - 1;
|
||||
data[data_length++] = x1 >> 8;
|
||||
data[data_length++] = x1 & 0xff;
|
||||
data[data_length++] = x2 >> 8;
|
||||
data[data_length++] = x2 & 0xff;
|
||||
}
|
||||
self->send(self->bus, false, self->always_toggle_chip_select, data, data_length);
|
||||
if (self->set_current_column_command != NO_COMMAND) {
|
||||
uint8_t command = self->set_current_column_command;
|
||||
self->send(self->bus, true, self->always_toggle_chip_select, &command, 1);
|
||||
self->send(self->bus, false, self->always_toggle_chip_select, data, data_length / 2);
|
||||
}
|
||||
|
||||
// Set row.
|
||||
data[0] = self->set_row_window_command;
|
||||
self->send(self->bus, true, self->always_toggle_chip_select, data, 1);
|
||||
data_length = 0;
|
||||
if (self->ram_height < 0x100) {
|
||||
data[data_length++] = y1 + self->rowstart;
|
||||
data[data_length++] = y2 - 1 + self->rowstart;
|
||||
} else {
|
||||
y1 += self->rowstart;
|
||||
y2 += self->rowstart - 1;
|
||||
data[data_length++] = y1 & 0xff;
|
||||
data[data_length++] = y1 >> 8;
|
||||
data[data_length++] = y2 & 0xff;
|
||||
data[data_length++] = y2 >> 8;
|
||||
}
|
||||
self->send(self->bus, false, self->always_toggle_chip_select, data, data_length);
|
||||
if (self->set_current_row_command != NO_COMMAND) {
|
||||
uint8_t command = self->set_current_row_command;
|
||||
self->send(self->bus, true, self->always_toggle_chip_select, &command, 1);
|
||||
self->send(self->bus, false, self->always_toggle_chip_select, data, data_length / 2);
|
||||
}
|
||||
}
|
||||
|
||||
void displayio_epaperdisplay_start_refresh(displayio_epaperdisplay_obj_t* self) {
|
||||
// run start sequence
|
||||
self->bus_reset(self->bus);
|
||||
self->core.bus_reset(self->core.bus);
|
||||
|
||||
send_command_sequence(self, true, self->start_sequence, self->start_sequence_len);
|
||||
self->last_refresh = ticks_ms;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
displayio_display_core_start_refresh(&self->core);
|
||||
}
|
||||
|
||||
uint32_t common_hal_displayio_epaperdisplay_get_time_to_refresh(displayio_epaperdisplay_obj_t* self) {
|
||||
// Refresh at seconds per frame rate.
|
||||
uint32_t elapsed_time = ticks_ms - self->last_refresh;
|
||||
uint32_t elapsed_time = ticks_ms - self->core.last_refresh;
|
||||
if (elapsed_time > self->milliseconds_per_frame) {
|
||||
return 0;
|
||||
}
|
||||
|
@ -380,20 +180,16 @@ uint32_t common_hal_displayio_epaperdisplay_get_time_to_refresh(displayio_epaper
|
|||
|
||||
void displayio_epaperdisplay_finish_refresh(displayio_epaperdisplay_obj_t* self) {
|
||||
// Actually refresh the display now that all pixel RAM has been updated.
|
||||
displayio_epaperdisplay_begin_transaction(self);
|
||||
self->send(self->bus, true, self->always_toggle_chip_select, &self->refresh_display_command, 1);
|
||||
displayio_epaperdisplay_end_transaction(self);
|
||||
displayio_display_core_begin_transaction(&self->core);
|
||||
self->core.send(self->core.bus, true, self->always_toggle_chip_select, &self->refresh_display_command, 1);
|
||||
displayio_display_core_end_transaction(&self->core);
|
||||
self->refreshing = true;
|
||||
|
||||
if (self->current_group != NULL) {
|
||||
displayio_group_finish_refresh(self->current_group);
|
||||
}
|
||||
self->refresh = false;
|
||||
self->full_refresh = false;
|
||||
self->last_refresh = ticks_ms;
|
||||
displayio_display_core_finish_refresh(&self->core);
|
||||
}
|
||||
|
||||
void displayio_epaperdisplay_send_pixels(displayio_epaperdisplay_obj_t* self, uint8_t* pixels, uint32_t length) {
|
||||
mp_obj_t common_hal_displayio_epaperdisplay_get_bus(displayio_epaperdisplay_obj_t* self) {
|
||||
return self->core.bus;
|
||||
}
|
||||
|
||||
bool displayio_epaperdisplay_refresh_area(displayio_epaperdisplay_obj_t* self, const displayio_area_t* area) {
|
||||
|
@ -401,12 +197,12 @@ bool displayio_epaperdisplay_refresh_area(displayio_epaperdisplay_obj_t* self, c
|
|||
|
||||
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_epaperdisplay_clip_area(self, area, &clipped)) {
|
||||
if (!displayio_display_core_clip_area(&self->core, 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) / self->colorspace.depth;
|
||||
uint8_t pixels_per_word = (sizeof(uint32_t) * 8) / self->core.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);
|
||||
|
@ -431,23 +227,23 @@ bool displayio_epaperdisplay_refresh_area(displayio_epaperdisplay_obj_t* self, c
|
|||
uint32_t mask[mask_length];
|
||||
|
||||
uint8_t passes = 1;
|
||||
if (self->colorspace.tricolor) {
|
||||
if (self->core.colorspace.tricolor) {
|
||||
passes = 2;
|
||||
}
|
||||
for (uint8_t pass = 0; pass < passes; pass++) {
|
||||
uint16_t remaining_rows = displayio_area_height(&clipped);
|
||||
|
||||
displayio_epaperdisplay_begin_transaction(self);
|
||||
displayio_epaperdisplay_set_region_to_update(self, &clipped);
|
||||
displayio_epaperdisplay_end_transaction(self);
|
||||
if (self->set_row_window_command != NO_COMMAND) {
|
||||
displayio_display_core_set_region_to_update(&self->core, self->set_column_window_command, self->set_row_window_command, self->set_current_column_command, self->set_current_row_command, false, self->always_toggle_chip_select, &clipped);
|
||||
}
|
||||
|
||||
uint8_t write_command = self->write_black_ram_command;
|
||||
if (pass == 1) {
|
||||
write_command = self->write_color_ram_command;
|
||||
}
|
||||
displayio_epaperdisplay_begin_transaction(self);
|
||||
self->send(self->bus, true, self->always_toggle_chip_select, &write_command, 1);
|
||||
displayio_epaperdisplay_end_transaction(self);
|
||||
displayio_display_core_begin_transaction(&self->core);
|
||||
self->core.send(self->core.bus, true, self->always_toggle_chip_select, &write_command, 1);
|
||||
displayio_display_core_end_transaction(&self->core);
|
||||
|
||||
for (uint16_t j = 0; j < subrectangles; j++) {
|
||||
displayio_area_t subrectangle = {
|
||||
|
@ -462,7 +258,7 @@ bool displayio_epaperdisplay_refresh_area(displayio_epaperdisplay_obj_t* self, c
|
|||
remaining_rows -= rows_per_buffer;
|
||||
|
||||
|
||||
uint16_t subrectangle_size_bytes = displayio_area_size(&subrectangle) / (8 / self->colorspace.depth);
|
||||
uint16_t subrectangle_size_bytes = displayio_area_size(&subrectangle) / (8 / self->core.colorspace.depth);
|
||||
|
||||
for (uint16_t k = 0; k < mask_length; k++) {
|
||||
mask[k] = 0x00000000;
|
||||
|
@ -471,11 +267,11 @@ bool displayio_epaperdisplay_refresh_area(displayio_epaperdisplay_obj_t* self, c
|
|||
buffer[k] = 0x00000000;
|
||||
}
|
||||
|
||||
self->colorspace.grayscale = true;
|
||||
self->core.colorspace.grayscale = true;
|
||||
if (pass == 1) {
|
||||
self->colorspace.grayscale = false;
|
||||
self->core.colorspace.grayscale = false;
|
||||
}
|
||||
displayio_group_fill_area(self->current_group, &self->colorspace, &subrectangle, mask, buffer);
|
||||
displayio_display_core_fill_area(&self->core, &subrectangle, mask, buffer);
|
||||
|
||||
// Invert it all.
|
||||
if ((pass == 1 && self->color_bits_inverted) ||
|
||||
|
@ -485,12 +281,12 @@ bool displayio_epaperdisplay_refresh_area(displayio_epaperdisplay_obj_t* self, c
|
|||
}
|
||||
}
|
||||
|
||||
if (!displayio_epaperdisplay_begin_transaction(self)) {
|
||||
if (!displayio_display_core_begin_transaction(&self->core)) {
|
||||
// Can't acquire display bus; skip the rest of the data. Try next display.
|
||||
return false;
|
||||
}
|
||||
self->send(self->bus, false, self->always_toggle_chip_select, (uint8_t*) buffer, subrectangle_size_bytes);
|
||||
displayio_epaperdisplay_end_transaction(self);
|
||||
self->core.send(self->core.bus, false, self->always_toggle_chip_select, (uint8_t*) buffer, subrectangle_size_bytes);
|
||||
displayio_display_core_end_transaction(&self->core);
|
||||
|
||||
// TODO(tannewt): Make refresh displays faster so we don't starve other
|
||||
// background tasks.
|
||||
|
@ -512,57 +308,47 @@ bool common_hal_displayio_epaperdisplay_refresh(displayio_epaperdisplay_obj_t* s
|
|||
return false;
|
||||
}
|
||||
}
|
||||
if (self->current_group == NULL) {
|
||||
return false;
|
||||
if (self->core.current_group == NULL) {
|
||||
return true;
|
||||
}
|
||||
// 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) {
|
||||
if (common_hal_displayio_epaperdisplay_get_time_to_refresh(self) > 0) {
|
||||
return false;
|
||||
}
|
||||
if (!displayio_epaperdisplay_bus_free(display)) {
|
||||
if (!displayio_display_core_bus_free(&self->core)) {
|
||||
// Can't acquire display bus; skip updating this display. Try next display.
|
||||
continue;
|
||||
return false;
|
||||
}
|
||||
const displayio_area_t* current_area = displayio_epaperdisplay_get_refresh_areas(display);
|
||||
const displayio_area_t* current_area = displayio_epaperdisplay_get_refresh_areas(self);
|
||||
if (current_area == NULL) {
|
||||
continue;
|
||||
return true;
|
||||
}
|
||||
displayio_epaperdisplay_start_refresh(display);
|
||||
displayio_epaperdisplay_start_refresh(self);
|
||||
while (current_area != NULL) {
|
||||
displayio_epaperdisplay_refresh_area(display, current_area);
|
||||
displayio_epaperdisplay_refresh_area(self, current_area);
|
||||
current_area = current_area->next;
|
||||
}
|
||||
displayio_epaperdisplay_finish_refresh(display);
|
||||
displayio_epaperdisplay_finish_refresh(self);
|
||||
return true;
|
||||
}
|
||||
|
||||
void displayio_epaperdisplay_background(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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void release_epaperdisplay(displayio_epaperdisplay_obj_t* self) {
|
||||
if (self->current_group != NULL) {
|
||||
self->current_group->in_group = false;
|
||||
}
|
||||
release_display_core(&self->core);
|
||||
if (self->busy.base.type == &digitalio_digitalinout_type) {
|
||||
common_hal_digitalio_digitalinout_deinit(&self->busy);
|
||||
}
|
||||
}
|
||||
|
||||
bool displayio_epaperdisplay_fill_area(displayio_epaperdisplay_obj_t *self, displayio_area_t* area, uint32_t* mask, uint32_t *buffer) {
|
||||
return displayio_group_fill_area(self->current_group, &self->colorspace, area, mask, buffer);
|
||||
}
|
||||
|
||||
bool displayio_epaperdisplay_clip_area(displayio_epaperdisplay_obj_t *self, const displayio_area_t* area, displayio_area_t* clipped) {
|
||||
bool overlaps = displayio_area_compute_overlap(&self->area, area, clipped);
|
||||
if (!overlaps) {
|
||||
return false;
|
||||
}
|
||||
// Expand the area if we have multiple pixels per byte and we need to byte
|
||||
// align the bounds.
|
||||
uint8_t pixels_per_byte = 8;
|
||||
if (clipped->x1 % pixels_per_byte != 0) {
|
||||
clipped->x1 -= clipped->x1 % pixels_per_byte;
|
||||
}
|
||||
if (clipped->x2 % pixels_per_byte != 0) {
|
||||
clipped->x2 += pixels_per_byte - clipped->x2 % pixels_per_byte;
|
||||
}
|
||||
return true;
|
||||
void displayio_epaperdisplay_collect_ptrs(displayio_epaperdisplay_obj_t* self) {
|
||||
displayio_display_core_collect_ptrs(&self->core);
|
||||
}
|
||||
|
|
|
@ -32,38 +32,17 @@
|
|||
#include "shared-bindings/pulseio/PWMOut.h"
|
||||
|
||||
#include "shared-module/displayio/area.h"
|
||||
|
||||
typedef void (*display_bus_bus_reset)(mp_obj_t bus);
|
||||
typedef bool (*display_bus_bus_free)(mp_obj_t bus);
|
||||
typedef bool (*display_bus_begin_transaction)(mp_obj_t bus);
|
||||
typedef void (*display_bus_send)(mp_obj_t bus, bool command, bool toggle_every_byte, uint8_t *data, uint32_t data_length);
|
||||
typedef void (*display_bus_end_transaction)(mp_obj_t bus);
|
||||
#include "shared-module/displayio/display_core.h"
|
||||
|
||||
typedef struct {
|
||||
mp_obj_base_t base;
|
||||
mp_obj_t bus;
|
||||
displayio_group_t *current_group;
|
||||
uint64_t last_refresh;
|
||||
display_bus_bus_reset bus_reset;
|
||||
display_bus_bus_free bus_free;
|
||||
display_bus_begin_transaction begin_transaction;
|
||||
display_bus_send send;
|
||||
display_bus_end_transaction end_transaction;
|
||||
displayio_display_core_t core;
|
||||
digitalio_digitalinout_obj_t busy;
|
||||
uint32_t milliseconds_per_frame;
|
||||
uint8_t* start_sequence;
|
||||
uint32_t start_sequence_len;
|
||||
uint8_t* stop_sequence;
|
||||
uint32_t stop_sequence_len;
|
||||
displayio_buffer_transform_t transform;
|
||||
displayio_area_t area;
|
||||
uint16_t width;
|
||||
uint16_t height;
|
||||
uint16_t ram_width;
|
||||
uint16_t ram_height;
|
||||
_displayio_colorspace_t colorspace;
|
||||
int16_t colstart;
|
||||
int16_t rowstart;
|
||||
uint16_t set_column_window_command;
|
||||
uint16_t set_row_window_command;
|
||||
uint16_t set_current_column_command;
|
||||
|
@ -76,11 +55,12 @@ typedef struct {
|
|||
bool black_bits_inverted;
|
||||
bool color_bits_inverted;
|
||||
bool refreshing;
|
||||
bool full_refresh; // New group means we need to refresh the whole display.
|
||||
bool always_toggle_chip_select;
|
||||
} displayio_epaperdisplay_obj_t;
|
||||
|
||||
void displayio_epaperdisplay_background(displayio_epaperdisplay_obj_t* self);
|
||||
void release_epaperdisplay(displayio_epaperdisplay_obj_t* self);
|
||||
|
||||
void displayio_epaperdisplay_collect_ptrs(displayio_epaperdisplay_obj_t* self);
|
||||
|
||||
#endif // MICROPY_INCLUDED_SHARED_MODULE_DISPLAYIO_EPAPERDISPLAY_H
|
||||
|
|
|
@ -4,7 +4,6 @@
|
|||
#include "shared-module/displayio/__init__.h"
|
||||
|
||||
#include "lib/utils/interrupt_char.h"
|
||||
#include "py/gc.h"
|
||||
#include "py/reload.h"
|
||||
#include "py/runtime.h"
|
||||
#include "shared-bindings/board/__init__.h"
|
||||
|
@ -16,7 +15,6 @@
|
|||
#include "supervisor/shared/autoreload.h"
|
||||
#include "supervisor/shared/display.h"
|
||||
#include "supervisor/memory.h"
|
||||
#include "supervisor/usb.h"
|
||||
|
||||
primary_display_t displays[CIRCUITPY_DISPLAY_LIMIT];
|
||||
|
||||
|
@ -46,8 +44,8 @@ void displayio_background(void) {
|
|||
}
|
||||
if (displays[i].display.base.type == &displayio_display_type) {
|
||||
displayio_display_background(&displays[i].display);
|
||||
} else if (displays[i].epaperdisplay.base.type == &displayio_epaperdisplay_type) {
|
||||
displayio_epaperdisplay_background(&displays[i].epaperdisplay);
|
||||
} else if (displays[i].epaper_display.base.type == &displayio_epaperdisplay_type) {
|
||||
displayio_epaperdisplay_background(&displays[i].epaper_display);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -162,9 +160,9 @@ void displayio_gc_collect(void) {
|
|||
// Alternatively, we could use gc_collect_root over the whole object,
|
||||
// but this is more precise, and is the only field that needs marking.
|
||||
if (displays[i].display.base.type == &displayio_display_type) {
|
||||
gc_collect_ptr(displays[i].display.current_group);
|
||||
displayio_display_collect_ptrs(&displays[i].display);
|
||||
} else if (displays[i].epaper_display.base.type == &displayio_epaperdisplay_type) {
|
||||
gc_collect_ptr(displays[i].epaper_display.current_group);
|
||||
displayio_epaperdisplay_collect_ptrs(&displays[i].epaper_display);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,309 @@
|
|||
/*
|
||||
* This file is part of the MicroPython project, http://micropython.org/
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2018 Scott Shawcroft for Adafruit Industries
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "shared-bindings/displayio/Display.h"
|
||||
|
||||
#include "py/gc.h"
|
||||
#include "py/runtime.h"
|
||||
#include "shared-bindings/displayio/FourWire.h"
|
||||
#include "shared-bindings/displayio/I2CDisplay.h"
|
||||
#include "shared-bindings/displayio/ParallelBus.h"
|
||||
#include "shared-bindings/microcontroller/Pin.h"
|
||||
#include "shared-bindings/time/__init__.h"
|
||||
#include "shared-module/displayio/__init__.h"
|
||||
#include "supervisor/shared/display.h"
|
||||
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "tick.h"
|
||||
|
||||
void displayio_display_core_construct(displayio_display_core_t* self,
|
||||
mp_obj_t bus, 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 color_depth, bool grayscale, bool pixels_in_byte_share_row, uint8_t bytes_per_cell, bool reverse_pixels_in_byte) {
|
||||
self->colorspace.depth = color_depth;
|
||||
self->colorspace.grayscale = grayscale;
|
||||
self->colorspace.pixels_in_byte_share_row = pixels_in_byte_share_row;
|
||||
self->colorspace.bytes_per_cell = bytes_per_cell;
|
||||
self->colorspace.reverse_pixels_in_byte = reverse_pixels_in_byte;
|
||||
self->current_group = NULL;
|
||||
self->colstart = colstart;
|
||||
self->rowstart = rowstart;
|
||||
|
||||
if (MP_OBJ_IS_TYPE(bus, &displayio_parallelbus_type)) {
|
||||
self->bus_reset = common_hal_displayio_parallelbus_reset;
|
||||
self->bus_free = common_hal_displayio_parallelbus_bus_free;
|
||||
self->begin_transaction = common_hal_displayio_parallelbus_begin_transaction;
|
||||
self->send = common_hal_displayio_parallelbus_send;
|
||||
self->end_transaction = common_hal_displayio_parallelbus_end_transaction;
|
||||
} else if (MP_OBJ_IS_TYPE(bus, &displayio_fourwire_type)) {
|
||||
self->bus_reset = common_hal_displayio_fourwire_reset;
|
||||
self->bus_free = common_hal_displayio_fourwire_bus_free;
|
||||
self->begin_transaction = common_hal_displayio_fourwire_begin_transaction;
|
||||
self->send = common_hal_displayio_fourwire_send;
|
||||
self->end_transaction = common_hal_displayio_fourwire_end_transaction;
|
||||
} else if (MP_OBJ_IS_TYPE(bus, &displayio_i2cdisplay_type)) {
|
||||
self->bus_reset = common_hal_displayio_i2cdisplay_reset;
|
||||
self->bus_free = common_hal_displayio_i2cdisplay_bus_free;
|
||||
self->begin_transaction = common_hal_displayio_i2cdisplay_begin_transaction;
|
||||
self->send = common_hal_displayio_i2cdisplay_send;
|
||||
self->end_transaction = common_hal_displayio_i2cdisplay_end_transaction;
|
||||
} else {
|
||||
mp_raise_ValueError(translate("Unsupported display bus type"));
|
||||
}
|
||||
self->bus = bus;
|
||||
|
||||
|
||||
supervisor_start_terminal(width, height);
|
||||
|
||||
self->width = width;
|
||||
self->height = height;
|
||||
self->ram_width = width;
|
||||
self->ram_height = height;
|
||||
rotation = rotation % 360;
|
||||
self->transform.x = 0;
|
||||
self->transform.y = 0;
|
||||
self->transform.scale = 1;
|
||||
self->transform.mirror_x = false;
|
||||
self->transform.mirror_y = false;
|
||||
self->transform.transpose_xy = false;
|
||||
if (rotation == 0 || rotation == 180) {
|
||||
if (rotation == 180) {
|
||||
self->transform.mirror_x = true;
|
||||
self->transform.mirror_y = true;
|
||||
}
|
||||
} else {
|
||||
self->transform.transpose_xy = true;
|
||||
if (rotation == 270) {
|
||||
self->transform.mirror_y = true;
|
||||
} else {
|
||||
self->transform.mirror_x = true;
|
||||
}
|
||||
}
|
||||
|
||||
self->area.x1 = 0;
|
||||
self->area.y1 = 0;
|
||||
self->area.next = NULL;
|
||||
|
||||
self->transform.dx = 1;
|
||||
self->transform.dy = 1;
|
||||
if (self->transform.transpose_xy) {
|
||||
self->area.x2 = height;
|
||||
self->area.y2 = width;
|
||||
if (self->transform.mirror_x) {
|
||||
self->transform.x = height;
|
||||
self->transform.dx = -1;
|
||||
}
|
||||
if (self->transform.mirror_y) {
|
||||
self->transform.y = width;
|
||||
self->transform.dy = -1;
|
||||
}
|
||||
} else {
|
||||
self->area.x2 = width;
|
||||
self->area.y2 = height;
|
||||
if (self->transform.mirror_x) {
|
||||
self->transform.x = width;
|
||||
self->transform.dx = -1;
|
||||
}
|
||||
if (self->transform.mirror_y) {
|
||||
self->transform.y = height;
|
||||
self->transform.dy = -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool displayio_display_core_show(displayio_display_core_t* self, displayio_group_t* root_group) {
|
||||
if (root_group == NULL) {
|
||||
if (!circuitpython_splash.in_group) {
|
||||
root_group = &circuitpython_splash;
|
||||
} else if (self->current_group == &circuitpython_splash) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (root_group == self->current_group) {
|
||||
return true;
|
||||
}
|
||||
if (root_group != NULL && root_group->in_group) {
|
||||
return false;
|
||||
}
|
||||
if (self->current_group != NULL) {
|
||||
self->current_group->in_group = false;
|
||||
}
|
||||
|
||||
if (root_group != NULL) {
|
||||
displayio_group_update_transform(root_group, &self->transform);
|
||||
root_group->in_group = true;
|
||||
}
|
||||
self->current_group = root_group;
|
||||
self->full_refresh = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
uint16_t displayio_display_core_get_width(displayio_display_core_t* self){
|
||||
return self->width;
|
||||
}
|
||||
|
||||
uint16_t displayio_display_core_get_height(displayio_display_core_t* self){
|
||||
return self->height;
|
||||
}
|
||||
|
||||
bool displayio_display_core_bus_free(displayio_display_core_t *self) {
|
||||
return self->bus_free(self->bus);
|
||||
}
|
||||
|
||||
bool displayio_display_core_begin_transaction(displayio_display_core_t* self) {
|
||||
return self->begin_transaction(self->bus);
|
||||
}
|
||||
|
||||
void displayio_display_core_end_transaction(displayio_display_core_t* self) {
|
||||
self->end_transaction(self->bus);
|
||||
}
|
||||
|
||||
void displayio_display_core_set_region_to_update(displayio_display_core_t* self, uint8_t column_command, uint8_t row_command, uint16_t set_current_column_command, uint16_t set_current_row_command, bool data_as_commands, bool always_toggle_chip_select, displayio_area_t* area) {
|
||||
displayio_display_core_begin_transaction(self);
|
||||
uint16_t x1 = area->x1;
|
||||
uint16_t x2 = area->x2;
|
||||
uint16_t y1 = area->y1;
|
||||
uint16_t y2 = area->y2;
|
||||
// Collapse down the dimension where multiple pixels are in a byte.
|
||||
if (self->colorspace.depth < 8) {
|
||||
uint8_t pixels_per_byte = 8 / self->colorspace.depth;
|
||||
if (self->colorspace.pixels_in_byte_share_row) {
|
||||
x1 /= pixels_per_byte * self->colorspace.bytes_per_cell;
|
||||
x2 /= pixels_per_byte * self->colorspace.bytes_per_cell;
|
||||
} else {
|
||||
y1 /= pixels_per_byte * self->colorspace.bytes_per_cell;
|
||||
y2 /= pixels_per_byte * self->colorspace.bytes_per_cell;
|
||||
}
|
||||
}
|
||||
|
||||
// Set column.
|
||||
uint8_t data[5];
|
||||
data[0] = column_command;
|
||||
uint8_t data_length = 1;
|
||||
if (!data_as_commands) {
|
||||
self->send(self->bus, true, true, data, 1);
|
||||
data_length = 0;
|
||||
}
|
||||
if (self->ram_width < 0x100) {
|
||||
data[data_length++] = x1 + self->colstart;
|
||||
data[data_length++] = x2 - 1 + self->colstart;
|
||||
} else {
|
||||
x1 += self->colstart;
|
||||
x2 += self->colstart - 1;
|
||||
data[data_length++] = x1 >> 8;
|
||||
data[data_length++] = x1 & 0xff;
|
||||
data[data_length++] = x2 >> 8;
|
||||
data[data_length++] = x2 & 0xff;
|
||||
}
|
||||
self->send(self->bus, data_as_commands, data_as_commands, data, data_length);
|
||||
if (set_current_column_command != NO_COMMAND) {
|
||||
uint8_t command = set_current_column_command;
|
||||
self->send(self->bus, true, always_toggle_chip_select, &command, 1);
|
||||
self->send(self->bus, false, always_toggle_chip_select, data, data_length / 2);
|
||||
}
|
||||
|
||||
// Set row.
|
||||
data[0] = row_command;
|
||||
data_length = 1;
|
||||
if (!data_as_commands) {
|
||||
self->send(self->bus, true, true, data, 1);
|
||||
data_length = 0;
|
||||
}
|
||||
if (self->ram_height < 0x100) {
|
||||
data[data_length++] = y1 + self->rowstart;
|
||||
data[data_length++] = y2 - 1 + self->rowstart;
|
||||
} else {
|
||||
y1 += self->rowstart;
|
||||
y2 += self->rowstart - 1;
|
||||
data[data_length++] = y1 >> 8;
|
||||
data[data_length++] = y1 & 0xff;
|
||||
data[data_length++] = y2 >> 8;
|
||||
data[data_length++] = y2 & 0xff;
|
||||
}
|
||||
self->send(self->bus, data_as_commands, data_as_commands, data, data_length);
|
||||
if (set_current_row_command != NO_COMMAND) {
|
||||
uint8_t command = set_current_row_command;
|
||||
self->send(self->bus, true, always_toggle_chip_select, &command, 1);
|
||||
self->send(self->bus, false, always_toggle_chip_select, data, data_length / 2);
|
||||
}
|
||||
|
||||
displayio_display_core_end_transaction(self);
|
||||
}
|
||||
|
||||
void displayio_display_core_start_refresh(displayio_display_core_t* self) {
|
||||
self->last_refresh = ticks_ms;
|
||||
}
|
||||
|
||||
void displayio_display_core_finish_refresh(displayio_display_core_t* self) {
|
||||
if (self->current_group != NULL) {
|
||||
displayio_group_finish_refresh(self->current_group);
|
||||
}
|
||||
self->full_refresh = false;
|
||||
self->last_refresh = ticks_ms;
|
||||
}
|
||||
|
||||
void release_display_core(displayio_display_core_t* self) {
|
||||
if (self->current_group != NULL) {
|
||||
self->current_group->in_group = false;
|
||||
}
|
||||
}
|
||||
|
||||
void displayio_display_core_collect_ptrs(displayio_display_core_t* self) {
|
||||
gc_collect_ptr(self->current_group);
|
||||
}
|
||||
|
||||
bool displayio_display_core_fill_area(displayio_display_core_t *self, displayio_area_t* area, uint32_t* mask, uint32_t *buffer) {
|
||||
return displayio_group_fill_area(self->current_group, &self->colorspace, area, mask, buffer);
|
||||
}
|
||||
|
||||
bool displayio_display_core_clip_area(displayio_display_core_t *self, const displayio_area_t* area, displayio_area_t* clipped) {
|
||||
bool overlaps = displayio_area_compute_overlap(&self->area, area, clipped);
|
||||
if (!overlaps) {
|
||||
return false;
|
||||
}
|
||||
// Expand the area if we have multiple pixels per byte and we need to byte
|
||||
// align the bounds.
|
||||
if (self->colorspace.depth < 8) {
|
||||
uint8_t pixels_per_byte = 8 / self->colorspace.depth * self->colorspace.bytes_per_cell;
|
||||
if (self->colorspace.pixels_in_byte_share_row) {
|
||||
if (clipped->x1 % pixels_per_byte != 0) {
|
||||
clipped->x1 -= clipped->x1 % pixels_per_byte;
|
||||
}
|
||||
if (clipped->x2 % pixels_per_byte != 0) {
|
||||
clipped->x2 += pixels_per_byte - clipped->x2 % pixels_per_byte;
|
||||
}
|
||||
} else {
|
||||
if (clipped->y1 % pixels_per_byte != 0) {
|
||||
clipped->y1 -= clipped->y1 % pixels_per_byte;
|
||||
}
|
||||
if (clipped->y2 % pixels_per_byte != 0) {
|
||||
clipped->y2 += pixels_per_byte - clipped->y2 % pixels_per_byte;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
|
@ -0,0 +1,90 @@
|
|||
/*
|
||||
* This file is part of the MicroPython project, http://micropython.org/
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2019 Scott Shawcroft for Adafruit Industries
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef MICROPY_INCLUDED_SHARED_MODULE_DISPLAYIO_DISPLAY_CORE_H
|
||||
#define MICROPY_INCLUDED_SHARED_MODULE_DISPLAYIO_DISPLAY_CORE_H
|
||||
|
||||
#include "shared-bindings/displayio/Group.h"
|
||||
|
||||
#include "shared-module/displayio/area.h"
|
||||
|
||||
#define NO_COMMAND 0x100
|
||||
|
||||
typedef void (*display_bus_bus_reset)(mp_obj_t bus);
|
||||
typedef bool (*display_bus_bus_free)(mp_obj_t bus);
|
||||
typedef bool (*display_bus_begin_transaction)(mp_obj_t bus);
|
||||
typedef void (*display_bus_send)(mp_obj_t bus, bool command, bool toggle_every_byte, uint8_t *data, uint32_t data_length);
|
||||
typedef void (*display_bus_end_transaction)(mp_obj_t bus);
|
||||
|
||||
typedef struct {
|
||||
mp_obj_t bus;
|
||||
displayio_group_t *current_group;
|
||||
uint64_t last_refresh;
|
||||
display_bus_bus_reset bus_reset;
|
||||
display_bus_bus_free bus_free;
|
||||
display_bus_begin_transaction begin_transaction;
|
||||
display_bus_send send;
|
||||
display_bus_end_transaction end_transaction;
|
||||
displayio_buffer_transform_t transform;
|
||||
displayio_area_t area;
|
||||
uint16_t width;
|
||||
uint16_t height;
|
||||
uint16_t rotation;
|
||||
uint16_t ram_width;
|
||||
uint16_t ram_height;
|
||||
_displayio_colorspace_t colorspace;
|
||||
int16_t colstart;
|
||||
int16_t rowstart;
|
||||
bool full_refresh; // New group means we need to refresh the whole display.
|
||||
} displayio_display_core_t;
|
||||
|
||||
void displayio_display_core_construct(displayio_display_core_t* self,
|
||||
mp_obj_t bus, 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 color_depth, bool grayscale, bool pixels_in_byte_share_row, uint8_t bytes_per_cell, bool reverse_pixels_in_byte);
|
||||
|
||||
bool displayio_display_core_show(displayio_display_core_t* self, displayio_group_t* root_group);
|
||||
|
||||
uint16_t displayio_display_core_get_width(displayio_display_core_t* self);
|
||||
uint16_t displayio_display_core_get_height(displayio_display_core_t* self);
|
||||
|
||||
bool displayio_display_core_bus_free(displayio_display_core_t *self);
|
||||
bool displayio_display_core_begin_transaction(displayio_display_core_t* self);
|
||||
void displayio_display_core_end_transaction(displayio_display_core_t* self);
|
||||
|
||||
void displayio_display_core_set_region_to_update(displayio_display_core_t* self, uint8_t column_command, uint8_t row_command, uint16_t set_current_column_command, uint16_t set_current_row_command, bool data_as_commands, bool always_toggle_chip_select, displayio_area_t* area);
|
||||
|
||||
void release_display_core(displayio_display_core_t* self);
|
||||
|
||||
void displayio_display_core_start_refresh(displayio_display_core_t* self);
|
||||
void displayio_display_core_finish_refresh(displayio_display_core_t* self);
|
||||
|
||||
void displayio_display_core_collect_ptrs(displayio_display_core_t* self);
|
||||
|
||||
bool displayio_display_core_fill_area(displayio_display_core_t *self, displayio_area_t* area, uint32_t* mask, uint32_t *buffer);
|
||||
|
||||
bool displayio_display_core_clip_area(displayio_display_core_t *self, const displayio_area_t* area, displayio_area_t* clipped);
|
||||
|
||||
#endif // MICROPY_INCLUDED_SHARED_MODULE_DISPLAYIO_DISPLAY_CORE_H
|
Loading…
Reference in New Issue