summaryrefslogtreecommitdiff
path: root/shared-module
diff options
context:
space:
mode:
authorLucian Copeland <hierophect@gmail.com>2020-04-15 10:29:05 -0400
committerLucian Copeland <hierophect@gmail.com>2020-04-15 10:29:05 -0400
commit54abfc2e8b88ed0dd1caaf3ac926466b0aba717a (patch)
treeb5154c84f807ee6ce3b0716b3a8d7b64dca46061 /shared-module
parent0f87a75f4f0675ff55e31437907101226d5fb66b (diff)
parentae549fc9da555778884ff64a2ffe45df0a5248db (diff)
translations-merge
Diffstat (limited to 'shared-module')
-rw-r--r--shared-module/_protomatter/Protomatter.c200
-rw-r--r--shared-module/_protomatter/Protomatter.h0
-rw-r--r--shared-module/_protomatter/__init__.c0
-rw-r--r--shared-module/_protomatter/__init__.h0
-rw-r--r--shared-module/_protomatter/allocator.h29
-rw-r--r--shared-module/displayio/Bitmap.c21
-rw-r--r--shared-module/displayio/ColorConverter.c11
-rw-r--r--shared-module/displayio/Display.c4
-rw-r--r--shared-module/displayio/EPaperDisplay.c2
-rw-r--r--shared-module/displayio/Palette.c7
-rw-r--r--shared-module/displayio/Palette.h1
-rw-r--r--shared-module/displayio/__init__.c84
-rw-r--r--shared-module/displayio/__init__.h15
-rw-r--r--shared-module/displayio/display_core.c46
-rw-r--r--shared-module/displayio/display_core.h2
-rw-r--r--shared-module/framebufferio/FramebufferDisplay.c300
-rw-r--r--shared-module/framebufferio/FramebufferDisplay.h81
-rw-r--r--shared-module/framebufferio/__init__.c0
-rw-r--r--shared-module/framebufferio/__init__.h0
19 files changed, 773 insertions, 30 deletions
diff --git a/shared-module/_protomatter/Protomatter.c b/shared-module/_protomatter/Protomatter.c
new file mode 100644
index 000000000..77ca63476
--- /dev/null
+++ b/shared-module/_protomatter/Protomatter.c
@@ -0,0 +1,200 @@
+/*
+ * This file is part of the Micro Python project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2020 Jeff Epler 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 <string.h>
+
+#include "py/gc.h"
+#include "py/obj.h"
+#include "py/objarray.h"
+#include "py/objproperty.h"
+#include "py/runtime.h"
+
+#include "common-hal/_protomatter/Protomatter.h"
+#include "shared-module/_protomatter/allocator.h"
+#include "shared-bindings/_protomatter/Protomatter.h"
+#include "shared-bindings/microcontroller/Pin.h"
+#include "shared-bindings/microcontroller/__init__.h"
+#include "shared-bindings/util.h"
+#include "shared-module/framebufferio/FramebufferDisplay.h"
+
+extern Protomatter_core *_PM_protoPtr;
+
+void common_hal_protomatter_protomatter_construct(protomatter_protomatter_obj_t *self, int width, int bit_depth, uint8_t rgb_count, uint8_t *rgb_pins, uint8_t addr_count, uint8_t *addr_pins, uint8_t clock_pin, uint8_t latch_pin, uint8_t oe_pin, bool doublebuffer, mp_obj_t framebuffer, void *timer) {
+ self->width = width;
+ self->bit_depth = bit_depth;
+ self->rgb_count = rgb_count;
+ memcpy(self->rgb_pins, rgb_pins, rgb_count);
+ self->addr_count = addr_count;
+ memcpy(self->addr_pins, addr_pins, addr_count);
+ self->clock_pin = clock_pin;
+ self->oe_pin = oe_pin;
+ self->latch_pin = latch_pin;
+ self->doublebuffer = doublebuffer;
+
+ self->timer = timer ? timer : common_hal_protomatter_timer_allocate();
+ if (self->timer == NULL) {
+ mp_raise_ValueError(translate("No timer available"));
+ }
+
+ self->width = width;
+ self->bufsize = 2 * width * rgb_count / 3 * (1 << addr_count);
+
+ common_hal_protomatter_protomatter_reconstruct(self, framebuffer);
+}
+
+void common_hal_protomatter_protomatter_reconstruct(protomatter_protomatter_obj_t* self, mp_obj_t framebuffer) {
+ if (framebuffer) {
+ self->framebuffer = framebuffer;
+ framebuffer = mp_obj_new_bytearray_of_zeros(self->bufsize);
+ mp_get_buffer_raise(self->framebuffer, &self->bufinfo, MP_BUFFER_READ);
+ if (mp_get_buffer(self->framebuffer, &self->bufinfo, MP_BUFFER_RW)) {
+ self->bufinfo.typecode = 'H' | MP_OBJ_ARRAY_TYPECODE_FLAG_RW;
+ } else {
+ self->bufinfo.typecode = 'H';
+ }
+ // verify that the matrix is big enough
+ mp_get_index(mp_obj_get_type(self->framebuffer), self->bufinfo.len, MP_OBJ_NEW_SMALL_INT(self->bufsize-1), false);
+ } else {
+ _PM_FREE(self->bufinfo.buf);
+ _PM_FREE(self->core.rgbPins);
+ _PM_FREE(self->core.addr);
+ _PM_FREE(self->core.screenData);
+
+ self->framebuffer = NULL;
+ self->bufinfo.buf = _PM_allocator_impl(self->bufsize);
+ self->bufinfo.len = self->bufsize;
+ self->bufinfo.typecode = 'H' | MP_OBJ_ARRAY_TYPECODE_FLAG_RW;
+ }
+
+ ProtomatterStatus stat = _PM_init(&self->core,
+ self->width, self->bit_depth,
+ self->rgb_count/6, self->rgb_pins,
+ self->addr_count, self->addr_pins,
+ self->clock_pin, self->latch_pin, self->oe_pin,
+ self->doublebuffer, self->timer);
+
+ if (stat == PROTOMATTER_OK) {
+ _PM_protoPtr = &self->core;
+ common_hal_mcu_disable_interrupts();
+ common_hal_protomatter_timer_enable(self->timer);
+ stat = _PM_begin(&self->core);
+ _PM_convert_565(&self->core, self->bufinfo.buf, self->width);
+ common_hal_mcu_enable_interrupts();
+ _PM_swapbuffer_maybe(&self->core);
+ }
+
+ if (stat != PROTOMATTER_OK) {
+ // XXX this deinit() actually makes crashy-crashy
+ // can trigger it by sending inappropriate pins
+ common_hal_protomatter_protomatter_deinit(self);
+ switch (stat) {
+ case PROTOMATTER_ERR_PINS:
+ mp_raise_ValueError(translate("Invalid pin"));
+ break;
+ case PROTOMATTER_ERR_ARG:
+ mp_raise_ValueError(translate("Invalid argument"));
+ break;
+ case PROTOMATTER_ERR_MALLOC: /// should have already been signaled as NLR
+ default:
+ mp_raise_msg_varg(&mp_type_RuntimeError,
+ translate("Protomatter internal error #%d"), (int)stat);
+ break;
+ }
+ }
+
+ self->paused = 0;
+
+}
+
+STATIC void free_pin(uint8_t *pin) {
+ if (*pin != COMMON_HAL_MCU_NO_PIN) {
+ common_hal_mcu_pin_reset_number(*pin);
+ }
+ *pin = COMMON_HAL_MCU_NO_PIN;
+}
+
+STATIC void free_pin_seq(uint8_t *seq, int count) {
+ for (int i=0; i<count; i++) {
+ free_pin(&seq[i]);
+ }
+}
+
+void common_hal_protomatter_protomatter_deinit(protomatter_protomatter_obj_t* self) {
+ if (self->timer) {
+ common_hal_protomatter_timer_free(self->timer);
+ self->timer = 0;
+ }
+
+ if (_PM_protoPtr == &self->core) {
+ _PM_protoPtr = NULL;
+ }
+
+ free_pin_seq(self->rgb_pins, self->rgb_count);
+ free_pin_seq(self->addr_pins, self->addr_count);
+ free_pin(&self->clock_pin);
+ free_pin(&self->latch_pin);
+ free_pin(&self->oe_pin);
+
+ if (self->core.rgbPins) {
+ _PM_free(&self->core);
+ }
+ memset(&self->core, 0, sizeof(self->core));
+
+ // If it was supervisor-allocated, it is supervisor-freed and the pointer
+ // is zeroed, otherwise the pointer is just zeroed
+ _PM_FREE(self->bufinfo.buf);
+ self->base.type = NULL;
+
+ // If a framebuffer was passed in to the constructor, NULL the reference
+ // here so that it will become GC'able
+ self->framebuffer = NULL;
+}
+
+void protomatter_protomatter_collect_ptrs(protomatter_protomatter_obj_t* self) {
+ gc_collect_ptr(self->framebuffer);
+ gc_collect_ptr(self->core.rgbPins);
+ gc_collect_ptr(self->core.addr);
+ gc_collect_ptr(self->core.screenData);
+}
+
+void common_hal_protomatter_protomatter_set_paused(protomatter_protomatter_obj_t* self, bool paused) {
+ if (paused && !self->paused) {
+ _PM_stop(&self->core);
+ } else if (!paused && self->paused) {
+ _PM_resume(&self->core);
+ }
+ self->paused = paused;
+}
+
+bool common_hal_protomatter_protomatter_get_paused(protomatter_protomatter_obj_t* self) {
+ return self->paused;
+}
+
+void common_hal_protomatter_protomatter_refresh(protomatter_protomatter_obj_t* self) {
+ _PM_convert_565(&self->core, self->bufinfo.buf, self->width);
+ _PM_swapbuffer_maybe(&self->core);
+}
+
diff --git a/shared-module/_protomatter/Protomatter.h b/shared-module/_protomatter/Protomatter.h
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/shared-module/_protomatter/Protomatter.h
diff --git a/shared-module/_protomatter/__init__.c b/shared-module/_protomatter/__init__.c
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/shared-module/_protomatter/__init__.c
diff --git a/shared-module/_protomatter/__init__.h b/shared-module/_protomatter/__init__.h
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/shared-module/_protomatter/__init__.h
diff --git a/shared-module/_protomatter/allocator.h b/shared-module/_protomatter/allocator.h
new file mode 100644
index 000000000..b7f517ce5
--- /dev/null
+++ b/shared-module/_protomatter/allocator.h
@@ -0,0 +1,29 @@
+#ifndef MICROPY_INCLUDED_SHARED_MODULE_PROTOMATTER_ALLOCATOR_H
+#define MICROPY_INCLUDED_SHARED_MODULE_PROTOMATTER_ALLOCATOR_H
+
+#include <stdbool.h>
+#include "py/gc.h"
+#include "py/misc.h"
+#include "supervisor/memory.h"
+
+#define _PM_ALLOCATOR _PM_allocator_impl
+#define _PM_FREE(x) (_PM_free_impl((x)), (x)=NULL, (void)0)
+
+static inline void *_PM_allocator_impl(size_t sz) {
+ if (gc_alloc_possible()) {
+ return m_malloc(sz + sizeof(void*), true);
+ } else {
+ supervisor_allocation *allocation = allocate_memory(align32_size(sz), false);
+ return allocation ? allocation->ptr : NULL;
+ }
+}
+
+static inline void _PM_free_impl(void *ptr_in) {
+ supervisor_allocation *allocation = allocation_from_ptr(ptr_in);
+
+ if (allocation) {
+ free_memory(allocation);
+ }
+}
+
+#endif
diff --git a/shared-module/displayio/Bitmap.c b/shared-module/displayio/Bitmap.c
index 59971d25c..8bcda6086 100644
--- a/shared-module/displayio/Bitmap.c
+++ b/shared-module/displayio/Bitmap.c
@@ -162,3 +162,24 @@ void displayio_bitmap_finish_refresh(displayio_bitmap_t *self) {
self->dirty_area.x1 = 0;
self->dirty_area.x2 = 0;
}
+
+void common_hal_displayio_bitmap_fill(displayio_bitmap_t *self, uint32_t value) {
+ if (self->read_only) {
+ mp_raise_RuntimeError(translate("Read-only object"));
+ }
+ // Update the dirty area.
+ self->dirty_area.x1 = 0;
+ self->dirty_area.x2 = self->width;
+ self->dirty_area.y1 = 0;
+ self->dirty_area.y2 = self->height;
+
+ // build the packed word
+ uint32_t word = 0;
+ for (uint8_t i=0; i<32 / self->bits_per_value; i++) {
+ word |= (value & self->bitmask) << (32 - ((i+1)*self->bits_per_value));
+ }
+ // copy it in
+ for (uint32_t i=0; i<self->stride * self->height; i++) {
+ self->data[i] = word;
+ }
+}
diff --git a/shared-module/displayio/ColorConverter.c b/shared-module/displayio/ColorConverter.c
index a5d7f0b05..2c9fb6d78 100644
--- a/shared-module/displayio/ColorConverter.c
+++ b/shared-module/displayio/ColorConverter.c
@@ -47,9 +47,7 @@ uint16_t displayio_colorconverter_compute_rgb565(uint32_t color_rgb888) {
uint32_t r5 = (color_rgb888 >> 19);
uint32_t g6 = (color_rgb888 >> 10) & 0x3f;
uint32_t b5 = (color_rgb888 >> 3) & 0x1f;
- uint32_t packed = r5 << 11 | g6 << 5 | b5;
- // swap bytes
- return __builtin_bswap16(packed);
+ return r5 << 11 | g6 << 5 | b5;
}
uint8_t displayio_colorconverter_compute_luma(uint32_t color_rgb888) {
@@ -156,7 +154,12 @@ void displayio_colorconverter_convert(displayio_colorconverter_t *self, const _d
}
if (colorspace->depth == 16) {
- output_color->pixel = displayio_colorconverter_compute_rgb565(pixel);
+ uint16_t packed = displayio_colorconverter_compute_rgb565(pixel);
+ if (colorspace->reverse_bytes_in_word) {
+ // swap bytes
+ packed = __builtin_bswap16(packed);
+ }
+ output_color->pixel = packed;
output_color->opaque = true;
return;
} else if (colorspace->tricolor) {
diff --git a/shared-module/displayio/Display.c b/shared-module/displayio/Display.c
index 2edded927..aaddbb471 100644
--- a/shared-module/displayio/Display.c
+++ b/shared-module/displayio/Display.c
@@ -46,7 +46,7 @@
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 bytes_per_cell, bool reverse_pixels_in_byte, bool reverse_bytes_in_word, 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,
@@ -60,7 +60,7 @@ void common_hal_displayio_display_construct(displayio_display_obj_t* self,
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);
+ color_depth, grayscale, pixels_in_byte_share_row, bytes_per_cell, reverse_pixels_in_byte, reverse_bytes_in_word);
self->set_column_command = set_column_command;
self->set_row_command = set_row_command;
diff --git a/shared-module/displayio/EPaperDisplay.c b/shared-module/displayio/EPaperDisplay.c
index 91d4bb7f9..5b4a8f916 100644
--- a/shared-module/displayio/EPaperDisplay.c
+++ b/shared-module/displayio/EPaperDisplay.c
@@ -58,7 +58,7 @@ void common_hal_displayio_epaperdisplay_construct(displayio_epaperdisplay_obj_t*
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);
+ displayio_display_core_construct(&self->core, bus, width, height, ram_width, ram_height, colstart, rowstart, rotation, 1, true, true, 1, true, true);
self->set_column_window_command = set_column_window_command;
self->set_row_window_command = set_row_window_command;
diff --git a/shared-module/displayio/Palette.c b/shared-module/displayio/Palette.c
index 3bce86f48..1ef03aab4 100644
--- a/shared-module/displayio/Palette.c
+++ b/shared-module/displayio/Palette.c
@@ -83,7 +83,12 @@ bool displayio_palette_get_color(displayio_palette_t *self, const _displayio_col
} else if (colorspace->grayscale) {
*color = self->colors[palette_index].luma >> (8 - colorspace->depth);
} else {
- *color = self->colors[palette_index].rgb565;
+ uint16_t packed = self->colors[palette_index].rgb565;
+ if (colorspace->reverse_bytes_in_word) {
+ // swap bytes
+ packed = __builtin_bswap16(packed);
+ }
+ *color = packed;
}
return true;
diff --git a/shared-module/displayio/Palette.h b/shared-module/displayio/Palette.h
index 758fd43fc..da72f250f 100644
--- a/shared-module/displayio/Palette.h
+++ b/shared-module/displayio/Palette.h
@@ -41,6 +41,7 @@ typedef struct {
bool tricolor;
bool pixels_in_byte_share_row;
bool reverse_pixels_in_byte;
+ bool reverse_bytes_in_word;
bool dither;
} _displayio_colorspace_t;
diff --git a/shared-module/displayio/__init__.c b/shared-module/displayio/__init__.c
index efa61265e..cf1bc45d9 100644
--- a/shared-module/displayio/__init__.c
+++ b/shared-module/displayio/__init__.c
@@ -21,6 +21,20 @@
primary_display_t displays[CIRCUITPY_DISPLAY_LIMIT];
+#if CIRCUITPY_PROTOMATTER
+STATIC bool any_display_uses_this_protomatter(protomatter_protomatter_obj_t* pm) {
+ for (uint8_t i = 0; i < CIRCUITPY_DISPLAY_LIMIT; i++) {
+ if (displays[i].framebuffer_display.base.type == &framebufferio_framebufferdisplay_type) {
+ framebufferio_framebufferdisplay_obj_t* display = &displays[i].framebuffer_display;
+ if (display->framebuffer == pm) {
+ return true;
+ }
+ }
+ }
+ return false;
+}
+#endif
+
// Check for recursive calls to displayio_background.
bool displayio_background_in_progress = false;
@@ -47,6 +61,10 @@ void displayio_background(void) {
}
if (displays[i].display.base.type == &displayio_display_type) {
displayio_display_background(&displays[i].display);
+#if CIRCUITPY_FRAMEBUFFERIO
+ } else if (displays[i].framebuffer_display.base.type == &framebufferio_framebufferdisplay_type) {
+ framebufferio_framebufferdisplay_background(&displays[i].framebuffer_display);
+#endif
} else if (displays[i].epaper_display.base.type == &displayio_epaperdisplay_type) {
displayio_epaperdisplay_background(&displays[i].epaper_display);
}
@@ -67,6 +85,10 @@ void common_hal_displayio_release_displays(void) {
release_display(&displays[i].display);
} else if (display_type == &displayio_epaperdisplay_type) {
release_epaperdisplay(&displays[i].epaper_display);
+#if CIRCUITPY_FRAMEBUFFERIO
+ } else if (display_type == &framebufferio_framebufferdisplay_type) {
+ release_framebufferdisplay(&displays[i].framebuffer_display);
+#endif
}
displays[i].display.base.type = &mp_type_NoneType;
}
@@ -80,6 +102,10 @@ void common_hal_displayio_release_displays(void) {
common_hal_displayio_i2cdisplay_deinit(&displays[i].i2cdisplay_bus);
} else if (bus_type == &displayio_parallelbus_type) {
common_hal_displayio_parallelbus_deinit(&displays[i].parallel_bus);
+#if CIRCUITPY_FRAMEBUFFERIO
+ } else if (bus_type == &protomatter_Protomatter_type) {
+ common_hal_protomatter_protomatter_deinit(&displays[i].protomatter);
+#endif
}
displays[i].fourwire_bus.base.type = &mp_type_NoneType;
}
@@ -141,6 +167,13 @@ void reset_displays(void) {
}
}
}
+#if CIRCUITPY_PROTOMATTER
+ } else if (displays[i].protomatter.base.type == &protomatter_Protomatter_type) {
+ protomatter_protomatter_obj_t * pm = &displays[i].protomatter;
+ if(!any_display_uses_this_protomatter(pm)) {
+ common_hal_protomatter_protomatter_deinit(pm);
+ }
+#endif
} else {
// Not an active display bus.
continue;
@@ -155,12 +188,24 @@ void reset_displays(void) {
} else if (displays[i].epaper_display.base.type == &displayio_epaperdisplay_type) {
displayio_epaperdisplay_obj_t* display = &displays[i].epaper_display;
common_hal_displayio_epaperdisplay_show(display, NULL);
+#if CIRCUITPY_FRAMEBUFFERIO
+ } else if (displays[i].framebuffer_display.base.type == &framebufferio_framebufferdisplay_type) {
+ framebufferio_framebufferdisplay_obj_t* display = &displays[i].framebuffer_display;
+ display->auto_refresh = true;
+ common_hal_framebufferio_framebufferdisplay_show(display, NULL);
+#endif
}
}
}
void displayio_gc_collect(void) {
for (uint8_t i = 0; i < CIRCUITPY_DISPLAY_LIMIT; i++) {
+#if CIRCUITPY_PROTOMATTER
+ if (displays[i].protomatter.base.type == &protomatter_Protomatter_type) {
+ protomatter_protomatter_collect_ptrs(&displays[i].protomatter);
+ }
+#endif
+
if (displays[i].display.base.type == NULL) {
continue;
}
@@ -169,6 +214,10 @@ void displayio_gc_collect(void) {
// but this is more precise, and is the only field that needs marking.
if (displays[i].display.base.type == &displayio_display_type) {
displayio_display_collect_ptrs(&displays[i].display);
+#if CIRCUITPY_FRAMEBUFFERIO
+ } else if (displays[i].framebuffer_display.base.type == &framebufferio_framebufferdisplay_type) {
+ framebufferio_framebufferdisplay_collect_ptrs(&displays[i].framebuffer_display);
+#endif
} else if (displays[i].epaper_display.base.type == &displayio_epaperdisplay_type) {
displayio_epaperdisplay_collect_ptrs(&displays[i].epaper_display);
}
@@ -308,3 +357,38 @@ void displayio_area_transform_within(bool mirror_x, bool mirror_y, bool transpos
transformed->x1 = whole->x1 + (y1 - whole->y1);
}
}
+
+primary_display_t *allocate_display(void) {
+ for (uint8_t i = 0; i < CIRCUITPY_DISPLAY_LIMIT; i++) {
+ mp_const_obj_t display_type = displays[i].display.base.type;
+ if (display_type == NULL || display_type == &mp_type_NoneType) {
+ return &displays[i];
+ }
+ }
+ return NULL;
+}
+
+primary_display_t *allocate_display_or_raise(void) {
+ primary_display_t *result = allocate_display();
+ if (result) {
+ return result;
+ }
+ mp_raise_RuntimeError(translate("Too many displays"));
+}
+primary_display_t *allocate_display_bus(void) {
+ for (uint8_t i = 0; i < CIRCUITPY_DISPLAY_LIMIT; i++) {
+ mp_const_obj_t display_type = displays[i].display.base.type;
+ if (display_type == NULL || display_type == &mp_type_NoneType) {
+ return &displays[i];
+ }
+ }
+ return NULL;
+}
+
+primary_display_t *allocate_display_bus_or_raise(void) {
+ primary_display_t *result = allocate_display_bus();
+ if (result) {
+ return result;
+ }
+ mp_raise_RuntimeError(translate("Too many display busses"));
+}
diff --git a/shared-module/displayio/__init__.h b/shared-module/displayio/__init__.h
index e78bc61ce..278a5e78f 100644
--- a/shared-module/displayio/__init__.h
+++ b/shared-module/displayio/__init__.h
@@ -29,20 +29,30 @@
#include "shared-bindings/displayio/Display.h"
#include "shared-bindings/displayio/EPaperDisplay.h"
+#if CIRCUITPY_FRAMEBUFFERIO
+#include "shared-bindings/framebufferio/FramebufferDisplay.h"
+#endif
#include "shared-bindings/displayio/FourWire.h"
#include "shared-bindings/displayio/Group.h"
#include "shared-bindings/displayio/I2CDisplay.h"
#include "shared-bindings/displayio/ParallelBus.h"
+#include "shared-bindings/_protomatter/Protomatter.h"
typedef struct {
union {
displayio_fourwire_obj_t fourwire_bus;
displayio_i2cdisplay_obj_t i2cdisplay_bus;
displayio_parallelbus_obj_t parallel_bus;
+#if CIRCUITPY_PROTOMATTER
+ protomatter_protomatter_obj_t protomatter;
+#endif
};
union {
displayio_display_obj_t display;
displayio_epaperdisplay_obj_t epaper_display;
+#if CIRCUITPY_FRAMEBUFFERIO
+ framebufferio_framebufferdisplay_obj_t framebuffer_display;
+#endif
};
} primary_display_t;
@@ -54,4 +64,9 @@ void displayio_background(void);
void reset_displays(void);
void displayio_gc_collect(void);
+primary_display_t *allocate_display(void);
+primary_display_t *allocate_display_or_raise(void);
+primary_display_t *allocate_display_bus(void);
+primary_display_t *allocate_display_bus_or_raise(void);
+
#endif // MICROPY_INCLUDED_SHARED_MODULE_DISPLAYIO___INIT___H
diff --git a/shared-module/displayio/display_core.c b/shared-module/displayio/display_core.c
index 120b70f76..4cc7564d0 100644
--- a/shared-module/displayio/display_core.c
+++ b/shared-module/displayio/display_core.c
@@ -44,38 +44,42 @@
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) {
+ uint16_t color_depth, bool grayscale, bool pixels_in_byte_share_row, uint8_t bytes_per_cell, bool reverse_pixels_in_byte, bool reverse_bytes_in_word) {
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->colorspace.reverse_bytes_in_word = reverse_bytes_in_word;
self->colorspace.dither = false;
self->current_group = NULL;
self->colstart = colstart;
self->rowstart = rowstart;
self->last_refresh = 0;
- 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"));
+ // (framebufferdisplay already validated its 'bus' is a buffer-protocol object)
+ if (bus) {
+ 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;
diff --git a/shared-module/displayio/display_core.h b/shared-module/displayio/display_core.h
index 1c3d0f09c..e4fd62d4a 100644
--- a/shared-module/displayio/display_core.h
+++ b/shared-module/displayio/display_core.h
@@ -58,7 +58,7 @@ typedef struct {
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);
+ uint16_t color_depth, bool grayscale, bool pixels_in_byte_share_row, uint8_t bytes_per_cell, bool reverse_pixels_in_byte, bool reverse_bytes_in_word);
bool displayio_display_core_show(displayio_display_core_t* self, displayio_group_t* root_group);
diff --git a/shared-module/framebufferio/FramebufferDisplay.c b/shared-module/framebufferio/FramebufferDisplay.c
new file mode 100644
index 000000000..bd0764e8e
--- /dev/null
+++ b/shared-module/framebufferio/FramebufferDisplay.c
@@ -0,0 +1,300 @@
+/*
+ * 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/framebufferio/FramebufferDisplay.h"
+
+#include "py/gc.h"
+#include "py/runtime.h"
+#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/shared/tick.h"
+#include "supervisor/usb.h"
+
+#include <stdint.h>
+#include <string.h>
+
+#include "tick.h"
+
+void common_hal_framebufferio_framebufferdisplay_construct(framebufferio_framebufferdisplay_obj_t* self,
+ mp_obj_t framebuffer, uint16_t width, uint16_t height,
+ uint16_t rotation, uint16_t color_depth,
+ uint8_t bytes_per_cell,
+ bool auto_refresh, uint16_t native_frames_per_second) {
+ // Turn off auto-refresh as we init.
+ self->auto_refresh = false;
+ self->framebuffer = framebuffer;
+ self->framebuffer_protocol = mp_proto_get_or_throw(MP_QSTR_protocol_framebuffer, framebuffer);
+
+ uint16_t ram_width = 0x100;
+ uint16_t ram_height = 0x100;
+
+ displayio_display_core_construct(&self->core, NULL, width, height, ram_width, ram_height, 0, 0, rotation,
+ color_depth, false, false, bytes_per_cell, false, false);
+
+ self->first_manual_refresh = !auto_refresh;
+
+ self->native_frames_per_second = native_frames_per_second;
+ self->native_ms_per_frame = 1000 / native_frames_per_second;
+
+ supervisor_start_terminal(width, height);
+
+ // Set the group after initialization otherwise we may send pixels while we delay in
+ // initialization.
+ common_hal_framebufferio_framebufferdisplay_show(self, &circuitpython_splash);
+ self->auto_refresh = auto_refresh;
+}
+
+bool common_hal_framebufferio_framebufferdisplay_show(framebufferio_framebufferdisplay_obj_t* self, displayio_group_t* root_group) {
+ return displayio_display_core_show(&self->core, root_group);
+}
+
+uint16_t common_hal_framebufferio_framebufferdisplay_get_width(framebufferio_framebufferdisplay_obj_t* self){
+ return displayio_display_core_get_width(&self->core);
+}
+
+uint16_t common_hal_framebufferio_framebufferdisplay_get_height(framebufferio_framebufferdisplay_obj_t* self){
+ return displayio_display_core_get_height(&self->core);
+}
+
+bool common_hal_framebufferio_framebufferdisplay_get_auto_brightness(framebufferio_framebufferdisplay_obj_t* self) {
+ if (self->framebuffer_protocol->get_auto_brightness) {
+ return self->framebuffer_protocol->get_auto_brightness(self->framebuffer);
+ }
+ return true;
+}
+
+bool common_hal_framebufferio_framebufferdisplay_set_auto_brightness(framebufferio_framebufferdisplay_obj_t* self, bool auto_brightness) {
+ if (self->framebuffer_protocol->set_auto_brightness) {
+ return self->framebuffer_protocol->set_auto_brightness(self->framebuffer, auto_brightness);
+ }
+ return false;
+}
+
+mp_float_t common_hal_framebufferio_framebufferdisplay_get_brightness(framebufferio_framebufferdisplay_obj_t* self) {
+ if (self->framebuffer_protocol->set_brightness) {
+ return self->framebuffer_protocol->get_brightness(self->framebuffer);
+ }
+ return -1;
+}
+
+bool common_hal_framebufferio_framebufferdisplay_set_brightness(framebufferio_framebufferdisplay_obj_t* self, mp_float_t brightness) {
+ bool ok = false;
+ if (self->framebuffer_protocol->set_brightness) {
+ self->framebuffer_protocol->set_brightness(self->framebuffer, brightness);
+ ok = true;
+ }
+ return ok;
+}
+
+mp_obj_t common_hal_framebufferio_framebufferdisplay_get_framebuffer(framebufferio_framebufferdisplay_obj_t* self) {
+ return self->framebuffer;
+}
+
+STATIC const displayio_area_t* _get_refresh_areas(framebufferio_framebufferdisplay_obj_t *self) {
+ if (self->core.full_refresh) {
+ self->core.area.next = NULL;
+ return &self->core.area;
+ } else if (self->core.current_group != NULL) {
+ return displayio_group_get_refresh_areas(self->core.current_group, NULL);
+ }
+ return NULL;
+}
+
+STATIC bool _refresh_area(framebufferio_framebufferdisplay_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_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->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);
+ if (rows_per_buffer == 0) {
+ rows_per_buffer = 1;
+ }
+ // If pixels are packed by column then ensure rows_per_buffer is on a byte boundary.
+ if (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;
+ }
+ }
+ subrectangles = displayio_area_height(&clipped) / rows_per_buffer;
+ if (displayio_area_height(&clipped) % rows_per_buffer != 0) {
+ subrectangles++;
+ }
+ pixels_per_buffer = rows_per_buffer * displayio_area_width(&clipped);
+ buffer_size = pixels_per_buffer / pixels_per_word;
+ if (pixels_per_buffer % pixels_per_word) {
+ buffer_size += 1;
+ }
+ }
+
+ // Allocated and shared as a uint32_t array so the compiler knows the
+ // alignment everywhere.
+ uint32_t buffer[buffer_size];
+ uint32_t mask_length = (pixels_per_buffer / 32) + 1;
+ uint32_t mask[mask_length];
+ uint16_t remaining_rows = displayio_area_height(&clipped);
+
+ for (uint16_t j = 0; j < subrectangles; j++) {
+ displayio_area_t subrectangle = {
+ .x1 = clipped.x1,
+ .y1 = clipped.y1 + rows_per_buffer * j,
+ .x2 = clipped.x2,
+ .y2 = clipped.y1 + rows_per_buffer * (j + 1)
+ };
+ if (remaining_rows < rows_per_buffer) {
+ subrectangle.y2 = subrectangle.y1 + remaining_rows;
+ }
+ remaining_rows -= rows_per_buffer;
+
+ memset(mask, 0, mask_length * sizeof(mask[0]));
+ memset(buffer, 0, buffer_size * sizeof(buffer[0]));
+
+ displayio_display_core_fill_area(&self->core, &subrectangle, mask, buffer);
+
+ // COULDDO: this arithmetic only supports multiple-of-8 bpp
+ uint8_t *dest = self->bufinfo.buf + (subrectangle.y1 * self->core.width + subrectangle.x1) * (self->core.colorspace.depth / 8);
+ uint8_t *src = (uint8_t*)buffer;
+ size_t rowsize = (subrectangle.x2 - subrectangle.x1) * (self->core.colorspace.depth / 8);
+ size_t rowstride = self->core.width * (self->core.colorspace.depth/8);
+ for (uint16_t i = subrectangle.y1; i < subrectangle.y2; i++) {
+ memcpy(dest, src, rowsize);
+ dest += rowstride;
+ src += rowsize;
+ }
+
+ // TODO(tannewt): Make refresh displays faster so we don't starve other
+ // background tasks.
+ usb_background();
+ }
+ return true;
+}
+
+STATIC void _refresh_display(framebufferio_framebufferdisplay_obj_t* self) {
+ displayio_display_core_start_refresh(&self->core);
+ self->framebuffer_protocol->get_bufinfo(self->framebuffer, &self->bufinfo);
+ const displayio_area_t* current_area = _get_refresh_areas(self);
+ while (current_area != NULL) {
+ _refresh_area(self, current_area);
+ current_area = current_area->next;
+ }
+ displayio_display_core_finish_refresh(&self->core);
+ self->framebuffer_protocol->swapbuffers(self->framebuffer);
+}
+
+void common_hal_framebufferio_framebufferdisplay_set_rotation(framebufferio_framebufferdisplay_obj_t* self, int rotation){
+ bool transposed = (self->core.rotation == 90 || self->core.rotation == 270);
+ bool will_transposed = (rotation == 90 || rotation == 270);
+ if(transposed != will_transposed) {
+ int tmp = self->core.width;
+ self->core.width = self->core.height;
+ self->core.height = tmp;
+ }
+ displayio_display_core_set_rotation(&self->core, rotation);
+ supervisor_stop_terminal();
+ supervisor_start_terminal(self->core.width, self->core.height);
+ if (self->core.current_group != NULL) {
+ displayio_group_update_transform(self->core.current_group, &self->core.transform);
+ }
+}
+
+uint16_t common_hal_framebufferio_framebufferdisplay_get_rotation(framebufferio_framebufferdisplay_obj_t* self){
+ return self->core.rotation;
+}
+
+
+bool common_hal_framebufferio_framebufferdisplay_refresh(framebufferio_framebufferdisplay_obj_t* self, uint32_t target_ms_per_frame, uint32_t maximum_ms_per_real_frame) {
+ if (!self->auto_refresh && !self->first_manual_refresh) {
+ uint64_t current_time = supervisor_ticks_ms64();
+ uint32_t current_ms_since_real_refresh = current_time - self->core.last_refresh;
+ // Test to see if the real frame time is below our minimum.
+ if (current_ms_since_real_refresh > maximum_ms_per_real_frame) {
+ mp_raise_RuntimeError(translate("Below minimum frame rate"));
+ }
+ uint32_t current_ms_since_last_call = current_time - self->last_refresh_call;
+ self->last_refresh_call = current_time;
+ // Skip the actual refresh to help catch up.
+ if (current_ms_since_last_call > target_ms_per_frame) {
+ return false;
+ }
+ uint32_t remaining_time = target_ms_per_frame - (current_ms_since_real_refresh % target_ms_per_frame);
+ // We're ahead of the game so wait until we align with the frame rate.
+ while (supervisor_ticks_ms64() - self->last_refresh_call < remaining_time) {
+ RUN_BACKGROUND_TASKS;
+ }
+ }
+ self->first_manual_refresh = false;
+ _refresh_display(self);
+ return true;
+}
+
+bool common_hal_framebufferio_framebufferdisplay_get_auto_refresh(framebufferio_framebufferdisplay_obj_t* self) {
+ return self->auto_refresh;
+}
+
+void common_hal_framebufferio_framebufferdisplay_set_auto_refresh(framebufferio_framebufferdisplay_obj_t* self,
+ bool auto_refresh) {
+ self->first_manual_refresh = !auto_refresh;
+ self->auto_refresh = auto_refresh;
+}
+
+STATIC void _update_backlight(framebufferio_framebufferdisplay_obj_t* self) {
+ // TODO(tannewt): Fade the backlight based on it's existing value and a target value. The target
+ // should account for ambient light when possible.
+}
+
+void framebufferio_framebufferdisplay_background(framebufferio_framebufferdisplay_obj_t* self) {
+ _update_backlight(self);
+
+ if (self->auto_refresh && (supervisor_ticks_ms64() - self->core.last_refresh) > self->native_ms_per_frame) {
+ _refresh_display(self);
+ }
+}
+
+void release_framebufferdisplay(framebufferio_framebufferdisplay_obj_t* self) {
+ release_display_core(&self->core);
+ self->framebuffer_protocol->deinit(self->framebuffer);
+}
+
+void reset_framebufferdisplay(framebufferio_framebufferdisplay_obj_t* self) {
+ self->auto_refresh = true;
+ common_hal_framebufferio_framebufferdisplay_show(self, NULL);
+}
+
+void framebufferio_framebufferdisplay_collect_ptrs(framebufferio_framebufferdisplay_obj_t* self) {
+ gc_collect_ptr(self->framebuffer);
+ displayio_display_core_collect_ptrs(&self->core);
+}
diff --git a/shared-module/framebufferio/FramebufferDisplay.h b/shared-module/framebufferio/FramebufferDisplay.h
new file mode 100644
index 000000000..a7b91a522
--- /dev/null
+++ b/shared-module/framebufferio/FramebufferDisplay.h
@@ -0,0 +1,81 @@
+/*
+ * 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_FRAMEBUFFERDISPLAY_H
+#define MICROPY_INCLUDED_SHARED_MODULE_DISPLAYIO_FRAMEBUFFERDISPLAY_H
+
+#include "py/obj.h"
+#include "py/proto.h"
+
+#include "shared-bindings/digitalio/DigitalInOut.h"
+#include "shared-bindings/displayio/Group.h"
+#include "shared-bindings/pulseio/PWMOut.h"
+
+#include "shared-module/displayio/area.h"
+#include "shared-module/displayio/display_core.h"
+
+typedef struct {
+ mp_obj_base_t base;
+ displayio_display_core_t core;
+ mp_obj_t framebuffer;
+ const struct _framebuffer_p_t *framebuffer_protocol;
+ mp_buffer_info_t bufinfo;
+ uint64_t last_backlight_refresh;
+ uint64_t last_refresh_call;
+ uint16_t native_frames_per_second;
+ uint16_t native_ms_per_frame;
+ bool auto_refresh;
+ bool first_manual_refresh;
+} framebufferio_framebufferdisplay_obj_t;
+
+void framebufferio_framebufferdisplay_background(framebufferio_framebufferdisplay_obj_t* self);
+void release_framebufferdisplay(framebufferio_framebufferdisplay_obj_t* self);
+void reset_framebufferdisplay(framebufferio_framebufferdisplay_obj_t* self);
+
+void framebufferio_framebufferdisplay_collect_ptrs(framebufferio_framebufferdisplay_obj_t* self);
+
+mp_obj_t common_hal_framebufferio_framebufferdisplay_get_framebuffer(framebufferio_framebufferdisplay_obj_t* self);
+
+typedef void (*framebuffer_get_bufinfo_fun)(mp_obj_t, mp_buffer_info_t *bufinfo);
+typedef void (*framebuffer_swapbuffers_fun)(mp_obj_t);
+typedef void (*framebuffer_deinit_fun)(mp_obj_t);
+typedef bool (*framebuffer_set_brightness_fun)(mp_obj_t, mp_float_t);
+typedef mp_float_t (*framebuffer_get_brightness_fun)(mp_obj_t);
+typedef bool (*framebuffer_set_auto_brightness_fun)(mp_obj_t, bool);
+typedef bool (*framebuffer_get_auto_brightness_fun)(mp_obj_t);
+
+typedef struct _framebuffer_p_t {
+ MP_PROTOCOL_HEAD // MP_QSTR_protocol_framebuffer
+ framebuffer_get_bufinfo_fun get_bufinfo;
+ framebuffer_swapbuffers_fun swapbuffers;
+ framebuffer_deinit_fun deinit;
+ framebuffer_get_brightness_fun get_brightness;
+ framebuffer_set_brightness_fun set_brightness;
+ framebuffer_get_auto_brightness_fun get_auto_brightness;
+ framebuffer_set_auto_brightness_fun set_auto_brightness;
+} framebuffer_p_t;
+
+#endif // MICROPY_INCLUDED_SHARED_MODULE_DISPLAYIO_FRAMEBUFFERDISPLAY_H
diff --git a/shared-module/framebufferio/__init__.c b/shared-module/framebufferio/__init__.c
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/shared-module/framebufferio/__init__.c
diff --git a/shared-module/framebufferio/__init__.h b/shared-module/framebufferio/__init__.h
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/shared-module/framebufferio/__init__.h