8000 Add support for display rotation and raw commands · tannewt/circuitpython@448ae64 · GitHub
[go: up one dir, main page]

Skip to content

Commit 448ae64

Browse files
committed
Add support for display rotation and raw commands
Display rotation is relative to the scan order of the display. The scan order can be found by scrolling the display with command 0x37 `display_bus.send(0x37, struct.pack(">H", i % 128))` Fixes micropython#1504
1 parent d72cd5b commit 448ae64

File tree

7 files changed

+147
-19
lines changed

7 files changed

+147
-19
lines changed

shared-bindings/displayio/Display.c

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -47,13 +47,16 @@
4747
//| objects in CircuitPython, Display objects live until `displayio.release_displays()`
4848
//| is called. This is done so that CircuitPython can use the display itself.
4949
//|
50+
//| Most people should not use this class directly. Use a specific display driver instead that will
51+
//| contain the initialization sequence at minimum.
52+
//|
5053
//| .. warning:: This will be changed before 4.0.0. Consider it very experimental.
5154
//|
52-
//| .. class:: Display(display_bus, init_sequence, *, width, height, colstart=0, rowstart=0, color_depth=16, set_column_command=0x2a, set_row_command=0x2b, write_ram_command=0x2c)
55+
//| .. class:: Display(display_bus, init_sequence, *, width, height, colstart=0, rowstart=0, rotation=0, color_depth=16, set_column_command=0x2a, set_row_command=0x2b, write_ram_command=0x2c, set_vertical_scroll=0, backlight_pin=None)
5356
//|
5457
//| Create a Display object on the given display bus (`displayio.FourWire` or `displayio.ParallelBus`).
5558
//|
56-
//| The ``init_sequence`` is bitbacked to minimize the ram impact. Every command begins with a
59+
//| The ``init_sequence`` is bitpacked to minimize the ram impact. Every command begins with a
5760
//| command byte followed by a byte to determine the parameter count and if a delay is need after.
5861
//| When the top bit of the second byte is 1, the next byte will be the delay time in milliseconds.
5962
//| The remaining 7 bits are the parameter count excluding any delay byte. The third through final
@@ -73,32 +76,39 @@
7376
//| (b"") are merged together on load. The parens are needed to allow byte literals on subsequent
7477
//| lines.
7578
//|
79+
//| The initialization sequence should always leave the display memory access inline with the scan
80+
//| of the display to minimize tearing artifacts.
81+
//|
7682
//| :param displayio.FourWire or displayio.ParallelBus display_bus: The bus that the display is connected to
7783
//| :param buffer init_sequence: Byte-packed initialization sequence.
7884
//| :param int width: Width in pixels
7985
//| :param int height: Height in pixels
8086
//| :param int colstart: The index if the first visible column
8187
//| :param int rowstart: The index if the first visible row
88+
//| :param int rotation: The rotation of the display in 90 degree increments
8289
//| :param int color_depth: The number of bits of color per pixel transmitted. (Some displays
8390
//| support 18 bit but 16 is easier to transmit. The last bit is extrapolated.)
8491
//| :param int set_column_command: Command used to set the start and end columns to update
8592
//| :param int set_row_command: Command used so set the start and end rows to update
8693
//| :param int write_ram_command: Command used to write pixels values into the update region
94+
//| :param int set_vertical_scroll: Command used to set the first row to show
8795
//| :param microcontroller.Pin backlight_pin: Pin connected to the display's backlight
8896
//|
8997
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) {
90-
enum { ARG_display_bus, ARG_init_sequence, ARG_width, ARG_height, ARG_colstart, ARG_rowstart, ARG_color_depth, ARG_set_column_command, ARG_set_row_command, ARG_write_ram_command, ARG_backlight_pin };
98+
enum { ARG_display_bus, ARG_init_sequence, ARG_width, ARG_height, ARG_colstart, ARG_rowstart, ARG_rotation, ARG_color_depth, ARG_set_column_command, ARG_set_row_command, ARG_write_ram_command, ARG_set_vertical_scroll, ARG_backlight_pin };
9199
static const mp_arg_t allowed_args[] = {
92100
{ MP_QSTR_display_bus, MP_ARG_REQUIRED | MP_ARG_OBJ },
93101
{ MP_QSTR_init_sequence, MP_ARG_REQUIRED | MP_ARG_OBJ },
94102
{ MP_QSTR_width, MP_ARG_INT | MP_ARG_KW_ONLY | MP_ARG_REQUIRED, },
95103
{ MP_QSTR_height, MP_ARG_INT | MP_ARG_KW_ONLY | MP_ARG_REQUIRED, },
96104
{ MP_QSTR_colstart, MP_ARG_INT | MP_ARG_KW_ONLY, {.u_int = 0} },
97105
{ MP_QSTR_rowstart, MP_ARG_INT | MP_ARG_KW_ONLY, {.u_int = 0} },
106+
{ MP_QSTR_rotation, MP_ARG_INT | MP_ARG_KW_ONLY, {.u_int = 0} },
98107
{ MP_QSTR_color_depth, MP_ARG_INT | MP_ARG_KW_ONLY, {.u_int = 16} },
99108
{ MP_QSTR_set_column_command, MP_ARG_INT | MP_ARG_KW_ONLY, {.u_int = 0x2a} },
100109
{ MP_QSTR_set_row_command, MP_ARG_INT | MP_ARG_KW_ONLY, {.u_int = 0x2b} },
101110
{ MP_QSTR_write_ram_command, MP_ARG_INT | MP_ARG_KW_ONLY, {.u_int = 0x2c} },
111+
{ MP_QSTR_set_vertical_scroll, MP_ARG_INT | MP_ARG_KW_ONLY, {.u_int = 0x0} },
102112
{ MP_QSTR_backlight_pin, MP_ARG_OBJ | MP_ARG_KW_ONLY, {.u_obj = mp_const_none} },
103113
};
104114
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
@@ -116,6 +126,10 @@ STATIC mp_obj_t displayio_display_make_new(const mp_obj_type_t *type, size_t n_a
116126
backlight_pin = MP_OBJ_TO_PTR(backlight_pin_obj);
117127
assert_pin_free(backlight_pin);
118128
}
129+
mp_int_t rotation = args[ARG_rotation].u_int;
130+
if (rotation % 90 != 0) {
131+
mp_raise_ValueError(translate("Display rotation must be in 90 degree increments"));
132+
}
119133

120134
displayio_display_obj_t *self = NULL;
121135
for (uint8_t i = 0; i < CIRCUITPY_DISPLAY_LIMIT; i++) {
@@ -130,9 +144,11 @@ STATIC mp_obj_t displayio_display_make_new(const mp_obj_type_t *type, size_t n_a
130144
}
131145
self->base.type = &displayio_display_type;
132146
common_hal_displayio_display_construct(self,
133-
display_bus, args[ARG_width].u_int, args[ARG_height].u_int, args[ARG_colstart].u_int, args[ARG_rowstart].u_int,
147+
display_bus, args[ARG_width].u_int, args[ARG_height].u_int, args[ARG_colstart].u_int, args[ARG_rowstart].u_int, rotation,
134148
args[ARG_color_depth].u_int, args[ARG_set_column_command].u_int, args[ARG_set_row_command].u_int,
135-
args[ARG_write_ram_command].u_int, bufinfo.buf, bufinfo.len, MP_OBJ_TO_PTR(backlight_pin));
149+
args[ARG_write_ram_command].u_int,
150+
args[ARG_set_vertical_scroll].u_int,
151+
bufinfo.buf, bufinfo.len, MP_OBJ_TO_PTR(backlight_pin));
136152

137153
return self;
138154
}

shared-bindings/displayio/Display.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,8 +38,8 @@ extern const mp_obj_type_t displayio_display_type;
3838

3939
void common_hal_displayio_display_construct(displayio_display_obj_t* self,
4040
mp_obj_t bus, uint16_t width, uint16_t height,
41-
int16_t colstart, int16_t rowstart, uint16_t color_depth,
42-
uint8_t set_column_command, uint8_t set_row_command, uint8_t write_ram_command,
41+
int16_t colstart, int16_t rowstart, uint16_t rotation, uint16_t color_depth,
42+
uint8_t set_column_command, uint8_t set_row_command, uint8_t write_ram_command, uint8_t set_vertical_scroll,
4343
uint8_t* init_sequence, uint16_t init_sequence_len, const mcu_pin_obj_t* backlight_pin);
4444

4545
int32_t common_hal_displayio_display_wait_for_frame(displayio_display_obj_t* self);

shared-bindings/displayio/FourWire.c

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,31 @@ STATIC mp_obj_t displayio_fourwire_make_new(const mp_obj_type_t *type, size_t n_
9898
return self;
9999
}
100100

101+
//| .. method:: send(command, data)
102+
//|
103+
//| Sends the given command value followed by the full set of data. Display state, such as
104+
//| vertical scroll, set via ``send`` may or may not be reset once the code is done.
105+
//|
106+
STATIC mp_obj_t displayio_fourwire_obj_send(mp_obj_t self, mp_obj_t command_obj, mp_obj_t data_obj) {
107+
mp_int_t command_int = MP_OBJ_SMALL_INT_VALUE(command_obj);
108+
if (!MP_OBJ_IS_SMALL_INT(command_obj) || command_int > 255 || command_int < 0) {
109+
mp_raise_ValueError(translate("Command must be an int between 0 and 255"));
110+
}
111+
uint8_t command = command_int;
112+
mp_buffer_info_t bufinfo;
113+
mp_get_buffer_raise(data_obj, &bufinfo, MP_BUFFER_READ);
114+
115+
common_hal_displayio_fourwire_begin_transaction(self);
116+
common_hal_displayio_fourwire_send(self, true, &command, 1);
117+
common_hal_displayio_fourwire_send(self, false, ((uint8_t*) bufinfo.buf), bufinfo.len);
118+
common_hal_displayio_fourwire_end_transaction(self);
119+
120+
return mp_const_none;
121+
}
122+
MP_DEFINE_CONST_FUN_OBJ_3(displayio_fourwire_send_obj, displayio_fourwire_obj_send);
123+
101124
STATIC const mp_rom_map_elem_t displayio_fourwire_locals_dict_table[] = {
125+
{ MP_ROM_QSTR(MP_QSTR_send), MP_ROM_PTR(&displayio_fourwire_send_obj) },
102126
};
103127
STATIC MP_DEFINE_CONST_DICT(displayio_fourwire_locals_dict, displayio_fourwire_locals_dict_table);
104128

shared-bindings/displayio/ParallelBus.c

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,31 @@ STATIC mp_obj_t displayio_parallelbus_make_new(const mp_obj_type_t *type, size_t
102102
return self;
103103
}
104104

105+
//| .. method:: send(command, data)
106+
//|
107+
//| Sends the given command value followed by the full set of data. Display state, such as
108+
//| vertical scroll, set via ``send`` may or may not be reset once the code is done.
109+
//|
110+
STATIC mp_obj_t displayio_parallelbus_obj_send(mp_obj_t self, mp_obj_t command_obj, mp_obj_t data_obj) {
111+
mp_int_t command_int = MP_OBJ_SMALL_INT_VALUE(command_obj);
112+
if (!MP_OBJ_IS_SMALL_INT(command_obj) || command_int > 255 || command_int < 0) {
113+
mp_raise_ValueError(translate("Command must be an int between 0 and 255"));
114+
}
115+
uint8_t command = command_int;
116+
mp_buffer_info_t bufinfo;
117+
mp_get_buffer_raise(data_obj, &bufinfo, MP_BUFFER_READ);
118+
119+
common_hal_displayio_parallelbus_begin_transaction(self);
120+
common_hal_displayio_parallelbus_send(self, true, &command, 1);
121+
common_hal_displayio_parallelbus_send(self, false, ((uint8_t*) bufinfo.buf), bufinfo.len);
122+
common_hal_displayio_parallelbus_end_transaction(self);
123+
124+
return mp_const_none;
125+
}
126+
MP_DEFINE_CONST_FUN_OBJ_3(displayio_parallelbus_send_obj, displayio_parallelbus_obj_send);
127+
105128
STATIC const mp_rom_map_elem_t displayio_parallelbus_locals_dict_table[] = {
129+
{ MP_ROM_QSTR(MP_QSTR_send), MP_ROM_PTR(&displayio_parallelbus_send_obj) },
106130
};
107131
STATIC MP_DEFINE_CONST_DICT(displayio_parallelbus_locals_dict, displayio_parallelbus_locals_dict_table);
108132

shared-module/displayio/Display.c

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -41,12 +41,10 @@
4141
#define DELAY 0x80
4242

4343
void common_hal_displayio_display_construct(displayio_display_obj_t* self,
44-
mp_obj_t bus, uint16_t width, uint16_t height, int16_t colstart, int16_t rowstart,
44+
mp_obj_t bus, uint16_t width, uint16_t height, int16_t colstart, int16_t rowstart, uint16_t rotation,
4545
uint16_t color_depth, uint8_t set_column_command, uint8_t set_row_command,
46-
uint8_t write_ram_command, uint8_t* init_sequence, uint16_t init_sequence_len,
46+
uint8_t write_ram_command, uint8_t set_vertical_scroll, uint8_t* init_sequence, uint16_t init_sequence_len,
4747
const mcu_pin_obj_t* backlight_pin) {
48-
self->width = width;
49-
self->height = height;
5048
self->color_depth = color_depth;
5149
self->set_column_command = set_column_command;
5250
self->set_row_command = set_row_command;
@@ -100,6 +98,26 @@ void common_hal_displayio_display_construct(displayio_display_obj_t* self,
10098
self->refresh = true;
10199
self->current_group = &circuitpython_splash;
102100

101+
self->width = width;
102+
self->height = height;
103+
rotation = rotation % 360;
104+
self->mirror_x = false;
105+
self->mirror_y = false;
106+
self->transpose_xy = false;
107+
if (rotation == 0 || rotation == 180) {
108+
if (rotation == 180) {
109+
self->mirror_x = true;
110+
self->mirror_y = true;
111+
}
112+
} else {
113+
self->transpose_xy = true;
114+
if (rotation == 90) {
115+
self->mirror_y = true;
116+
} else {
117+
self->mirror_x = true;
118+
}
119+
}
120+
103121
// Always set the backlight type in case we're reusing memory.
104122
self->backlight_inout.base.type = &mp_type_NoneType;
105123
if (backlight_pin != NULL && common_hal_mcu_pin_is_free(backlight_pin)) {

shared-module/displayio/Display.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,9 @@ typedef struct {
5959
uint64_t last_backlight_refresh;
6060
bool auto_brightness:1;
6161
bool updating_backlight:1;
62+
bool mirror_x;
63+
bool mirror_y;
64+
bool transpose_xy;
6265
} displayio_display_obj_t;
6366

6467
void displayio_display_update_backlight(displayio_display_obj_t* self);

shared-module/displayio/__init__.c

Lines changed: 51 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,12 @@
1212

1313
primary_display_t displays[CIRCUITPY_DISPLAY_LIMIT];
1414

15+
static inline void swap(uint16_t* a, uint16_t* b) {
16+
uint16_t temp = *a;
17+
*a = *b;
18+
*b = temp;
19+
}
20+
1521
void displayio_refresh_displays(void) {
1622
for (uint8_t i = 0; i < CIRCUITPY_DISPLAY_LIMIT; i++) {
1723
if (displays[i].display.base.type == NULL || displays[i].display.base.type == &mp_type_NoneType) {
@@ -24,23 +30,60 @@ void displayio_refresh_displays(void) {
2430
return;
2531
}
2632
if (displayio_display_refresh_queued(display)) {
27-
// We compute the pixels
33+
// We compute the pixels. r and c are row and column to match the display memory
34+
// structure. x and y match location within the groups.
35+
uint16_t c0 = 0;
36+
uint16_t r0 = 0;
37+
uint16_t c1 = display->width;
38+
uint16_t r1 = display->height;
39+
if (display->transpose_xy) {
40+
swap(& 10000 amp;c1, &r1);
41+
}
42+
displayio_display_start_region_update(display, c0, r0, c1, r1);
43+
2844
uint16_t x0 = 0;
45+
uint16_t x1 = display->width - 1;
46+
uint16_t startx = 0;
47+
int8_t dx = 1;
48+
if (display->mirror_x) {
49+
dx = -1;
50+
startx = x1;
51+
}
2952
uint16_t y0 = 0;
30-
uint16_t x1 = display->width;
31-
uint16_t y1 = display->height;
53+
uint16_t y1 = display->height - 1;
54+
uint16_t starty = 0;
55+
int8_t dy = 1;
56+
if (display->mirror_y) {
57+
dy = -1;
58+
starty = y1;
59+
}
60+
61+
bool transpose = false;
62+
if (display->transpose_xy) {
63+
transpose = true;
64+
int8_t temp_dx = dx;
65+
dx = dy;
66+
dy = temp_dx;
67+
68+
swap(&starty, &startx);
69+
swap(&x0, &y0);
70+
swap(&x1, &y1);
71+
}
72+
3273
size_t index = 0;
33-
//size_t row_size = (x1 - x0);
3474
uint16_t buffer_size = 256;
3575
uint32_t buffer[buffer_size / 2];
36-
displayio_display_start_region_update(display, x0, y0, x1, y1);
37-
for (uint16_t y = y0; y < y1; ++y) {
38-
for (uint16_t x = x0; x < x1; ++x) {
76+
for (uint16_t y = starty; y0 <= y && y <= y1; y += dy) {
77+
for (uint16_t x = startx; x0 <= x && x <= x1; x += dx) {
3978
uint16_t* pixel = &(((uint16_t*)buffer)[index]);
4079
*pixel = 0;
4180

4281
if (display->current_group != NULL) {
43-
displayio_group_get_pixel(display->current_group, x, y, pixel);
82+
if (transpose) {
83+
displayio_group_get_pixel(display->current_group, y, x, pixel);
84+
} else {
85+
displayio_group_get_pixel(display->current_group, x, y, pixel);
86+
}
4487
}
4588

4689
index += 1;

0 commit comments

Comments
 (0)
0