From 674a4bbe274b677f2ae1dc4ac02e05458b71e851 Mon Sep 17 00:00:00 2001 From: Mike Bell Date: Sun, 17 Nov 2024 15:38:26 +0000 Subject: [PATCH 1/7] Add display backlight control. --- drivers/st7701/st7701.cpp | 21 ++++++--------------- drivers/st7701/st7701.hpp | 1 + modules/c/presto/presto.c | 2 ++ modules/c/presto/presto.cpp | 12 ++++++++++++ modules/c/presto/presto.h | 3 ++- 5 files changed, 23 insertions(+), 16 deletions(-) diff --git a/drivers/st7701/st7701.cpp b/drivers/st7701/st7701.cpp index ee57551..81df60a 100644 --- a/drivers/st7701/st7701.cpp +++ b/drivers/st7701/st7701.cpp @@ -349,17 +349,11 @@ void ST7701::init_framebuffer() { // if a backlight pin is provided then set it up for // pwm control if(lcd_bl != PIN_UNUSED) { - #if 0 pwm_config cfg = pwm_get_default_config(); - pwm_set_wrap(pwm_gpio_to_slice_num(lcd_bl), 65535); + pwm_config_set_wrap(&cfg, BACKLIGHT_PWM_TOP); pwm_init(pwm_gpio_to_slice_num(lcd_bl), &cfg, true); gpio_set_function(lcd_bl, GPIO_FUNC_PWM); set_backlight(0); // Turn backlight off initially to avoid nasty surprises - #else - gpio_init(lcd_bl); - gpio_set_dir(lcd_bl, GPIO_OUT); - gpio_put(lcd_bl, 0); - #endif } command(reg::SWRESET); @@ -430,11 +424,7 @@ void ST7701::init_framebuffer() { if(lcd_bl != PIN_UNUSED) { //update(); // Send the new buffer to the display to clear any previous content sleep_ms(50); // Wait for the update to apply - #if 0 set_backlight(255); // Turn backlight on now surprises have passed - #else - gpio_put(lcd_bl, 1); - #endif } } @@ -550,10 +540,11 @@ void ST7701::init_framebuffer() { } void ST7701::set_backlight(uint8_t brightness) { - // gamma correct the provided 0-255 brightness value onto a - // 0-65535 range for the pwm counter - float gamma = 2.8; - uint16_t value = (uint16_t)(pow((float)(brightness) / 255.0f, gamma) * 65535.0f + 0.5f); + // At least on my hardware this gives reasonable control over the possible range of backlight brightness + uint16_t value; + if (brightness == 0) value = 0; + else if (brightness == 255) value = BACKLIGHT_PWM_TOP; + else value = 181 + (brightness * brightness) / 85; pwm_set_gpio_level(lcd_bl, value); } diff --git a/drivers/st7701/st7701.hpp b/drivers/st7701/st7701.hpp index d1218b4..622a8ef 100644 --- a/drivers/st7701/st7701.hpp +++ b/drivers/st7701/st7701.hpp @@ -47,6 +47,7 @@ namespace pimoroni { uint lcd_dot_clk = 22; static const uint32_t SPI_BAUD = 8'000'000; + static const uint32_t BACKLIGHT_PWM_TOP = 6200; public: // Parallel init diff --git a/modules/c/presto/presto.c b/modules/c/presto/presto.c index a4bab70..7a8511d 100644 --- a/modules/c/presto/presto.c +++ b/modules/c/presto/presto.c @@ -5,12 +5,14 @@ MP_DEFINE_CONST_FUN_OBJ_1(Presto___del___obj, Presto___del__); MP_DEFINE_CONST_FUN_OBJ_2(Presto_update_obj, Presto_update); +MP_DEFINE_CONST_FUN_OBJ_2(Presto_set_backlight_obj, Presto_set_backlight); /***** Binding of Methods *****/ static const mp_rom_map_elem_t Presto_locals_dict_table[] = { { MP_ROM_QSTR(MP_QSTR___del__), MP_ROM_PTR(&Presto___del___obj) }, { MP_ROM_QSTR(MP_QSTR_update), MP_ROM_PTR(&Presto_update_obj) }, + { MP_ROM_QSTR(MP_QSTR_set_backlight), MP_ROM_PTR(&Presto_set_backlight_obj) }, { MP_ROM_QSTR(MP_QSTR_WIDTH), MP_ROM_INT(WIDTH) }, { MP_ROM_QSTR(MP_QSTR_HEIGHT), MP_ROM_INT(HEIGHT) }, diff --git a/modules/c/presto/presto.cpp b/modules/c/presto/presto.cpp index fc38d10..b3cd1cb 100644 --- a/modules/c/presto/presto.cpp +++ b/modules/c/presto/presto.cpp @@ -159,6 +159,18 @@ extern mp_obj_t Presto_update(mp_obj_t self_in, mp_obj_t graphics_in) { return mp_const_none; } +mp_obj_t Presto_set_backlight(mp_obj_t self_in, mp_obj_t brightness) { + _Presto_obj_t *self = MP_OBJ_TO_PTR2(self_in, _Presto_obj_t); + + float b = mp_obj_get_float(brightness); + + if(b < 0 || b > 1.0f) mp_raise_ValueError("brightness out of range. Expected 0.0 to 1.0"); + + self->presto->set_backlight((uint8_t)(b * 255.0f)); + + return mp_const_none; +} + mp_obj_t Presto___del__(mp_obj_t self_in) { (void)self_in; //_Presto_obj_t *self = MP_OBJ_TO_PTR2(self_in, _Presto_obj_t); diff --git a/modules/c/presto/presto.h b/modules/c/presto/presto.h index b153651..95127ec 100644 --- a/modules/c/presto/presto.h +++ b/modules/c/presto/presto.h @@ -2,7 +2,7 @@ #include "py/runtime.h" /***** Constants *****/ -static const uint BACKLIGHT = 38; +static const uint BACKLIGHT = 45; static const int WIDTH = 240; static const int HEIGHT = 240; @@ -19,4 +19,5 @@ extern const mp_obj_type_t Presto_type; extern mp_obj_t Presto_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args); extern mp_obj_t Presto_update(mp_obj_t self_in, mp_obj_t graphics_in); extern mp_int_t Presto_get_framebuffer(mp_obj_t self_in, mp_buffer_info_t *bufinfo, mp_uint_t flags); +extern mp_obj_t Presto_set_backlight(mp_obj_t self_in, mp_obj_t brightness); extern mp_obj_t Presto___del__(mp_obj_t self_in); \ No newline at end of file From 43a660626a5fb1fa6b06203e4b39beb6b15f245a Mon Sep 17 00:00:00 2001 From: Mike Bell Date: Sun, 17 Nov 2024 15:38:26 +0000 Subject: [PATCH 2/7] MicroPython: Full res option. --- modules/c/presto/presto.c | 6 ++++-- modules/c/presto/presto.cpp | 28 +++++++++++++++++++--------- modules/c/presto/presto.h | 4 ++-- presto/mpconfigboard.h | 1 + 4 files changed, 26 insertions(+), 13 deletions(-) diff --git a/modules/c/presto/presto.c b/modules/c/presto/presto.c index 7a8511d..8cfbc3b 100644 --- a/modules/c/presto/presto.c +++ b/modules/c/presto/presto.c @@ -14,8 +14,10 @@ static const mp_rom_map_elem_t Presto_locals_dict_table[] = { { MP_ROM_QSTR(MP_QSTR_update), MP_ROM_PTR(&Presto_update_obj) }, { MP_ROM_QSTR(MP_QSTR_set_backlight), MP_ROM_PTR(&Presto_set_backlight_obj) }, - { MP_ROM_QSTR(MP_QSTR_WIDTH), MP_ROM_INT(WIDTH) }, - { MP_ROM_QSTR(MP_QSTR_HEIGHT), MP_ROM_INT(HEIGHT) }, + { MP_ROM_QSTR(MP_QSTR_WIDTH), MP_ROM_INT(WIDTH/2) }, + { MP_ROM_QSTR(MP_QSTR_HEIGHT), MP_ROM_INT(HEIGHT/2) }, + { MP_ROM_QSTR(MP_QSTR_FULL_WIDTH), MP_ROM_INT(WIDTH) }, + { MP_ROM_QSTR(MP_QSTR_FULL_HEIGHT), MP_ROM_INT(HEIGHT) }, }; static MP_DEFINE_CONST_DICT(Presto_locals_dict, Presto_locals_dict_table); diff --git a/modules/c/presto/presto.cpp b/modules/c/presto/presto.cpp index b3cd1cb..74e2dc3 100644 --- a/modules/c/presto/presto.cpp +++ b/modules/c/presto/presto.cpp @@ -20,8 +20,7 @@ extern "C" { // MicroPython's GC heap will automatically resize, so we should just // statically allocate these in C++ to avoid fragmentation. -__attribute__((section(".uninitialized_data"))) static uint16_t presto_buffer_a[WIDTH * HEIGHT] = {0}; -__attribute__((section(".uninitialized_data"))) static uint16_t presto_buffer_b[WIDTH * HEIGHT] = {0}; +__attribute__((section(".uninitialized_data"))) static uint16_t presto_buffer[WIDTH * HEIGHT] = {0}; void __printf_debug_flush() { for(auto i = 0u; i < 10; i++) { @@ -47,6 +46,8 @@ typedef struct _Presto_obj_t { ST7701* presto; uint16_t* next_fb; uint16_t* curr_fb; + uint16_t width; + uint16_t height; } _Presto_obj_t; typedef struct _ModPicoGraphics_obj_t { @@ -79,10 +80,9 @@ static uint32_t core1_stack[stack_size] = {0}; mp_obj_t Presto_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) { _Presto_obj_t *self = nullptr; - enum { ARG_pio, ARG_sm, ARG_pins, ARG_common_pin, ARG_direction, ARG_counts_per_rev, ARG_count_microsteps, ARG_freq_divider }; + enum { ARG_full_res }; static const mp_arg_t allowed_args[] = { - { MP_QSTR_pio, MP_ARG_INT }, - { MP_QSTR_sm, MP_ARG_INT } + { MP_QSTR_full_res, MP_ARG_BOOL, {.u_bool = false} } }; // Parse args. @@ -93,11 +93,21 @@ mp_obj_t Presto_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, self = mp_obj_malloc_with_finaliser(_Presto_obj_t, &Presto_type); presto_debug("set fb pointers\n"); - self->curr_fb = presto_buffer_a; //(uint16_t*)0x11000000; - self->next_fb = presto_buffer_b; //(uint16_t*)0x11080000; + self->curr_fb = presto_buffer; + self->next_fb = presto_buffer; + + if (!args[ARG_full_res].u_bool) { + self->width = WIDTH / 2; + self->height = HEIGHT / 2; + self->next_fb += (WIDTH * HEIGHT) / 4; + } + else { + self->width = WIDTH; + self->height = HEIGHT; + } presto_debug("m_new_class(ST7701...\n"); - ST7701 *presto = m_new_class(ST7701, WIDTH, HEIGHT, ROTATE_0, + ST7701 *presto = m_new_class(ST7701, self->width, self->height, ROTATE_0, SPIPins{spi1, LCD_CS, LCD_CLK, LCD_DAT, PIN_UNUSED, LCD_DC, BACKLIGHT}, self->next_fb, LCD_D0); @@ -141,7 +151,7 @@ mp_int_t Presto_get_framebuffer(mp_obj_t self_in, mp_buffer_info_t *bufinfo, mp_ _Presto_obj_t *self = MP_OBJ_TO_PTR2(self_in, _Presto_obj_t); (void)flags; bufinfo->buf = self->curr_fb; - bufinfo->len = WIDTH * HEIGHT * 2; + bufinfo->len = self->width * self->height * 2; bufinfo->typecode = 'B'; return 0; } diff --git a/modules/c/presto/presto.h b/modules/c/presto/presto.h index 95127ec..3c72a98 100644 --- a/modules/c/presto/presto.h +++ b/modules/c/presto/presto.h @@ -4,8 +4,8 @@ /***** Constants *****/ static const uint BACKLIGHT = 45; -static const int WIDTH = 240; -static const int HEIGHT = 240; +static const int WIDTH = 480; +static const int HEIGHT = 480; static const uint LCD_CLK = 26; static const uint LCD_CS = 28; static const uint LCD_DAT = 27; diff --git a/presto/mpconfigboard.h b/presto/mpconfigboard.h index ea5ee1e..c7c5f25 100644 --- a/presto/mpconfigboard.h +++ b/presto/mpconfigboard.h @@ -33,6 +33,7 @@ int mp_hal_is_pin_reserved(int n); #define MICROPY_HW_ENABLE_PSRAM (1) #define MICROPY_PY_THREAD (0) +#define MICROPY_GC_SPLIT_HEAP (0) // TODO: Remove when https://github.com/micropython/micropython/pull/15655 is merged #define core1_entry (NULL) \ No newline at end of file From b896b4ef08ea3bf1d040a5f01db019e84ff33ff3 Mon Sep 17 00:00:00 2001 From: Mike Bell Date: Sun, 17 Nov 2024 15:38:26 +0000 Subject: [PATCH 3/7] Examples: Full res vector clock example. --- examples/vector_clock_full.py | 138 ++++++++++++++++++++++++++++++++++ 1 file changed, 138 insertions(+) create mode 100644 examples/vector_clock_full.py diff --git a/examples/vector_clock_full.py b/examples/vector_clock_full.py new file mode 100644 index 0000000..7684ca5 --- /dev/null +++ b/examples/vector_clock_full.py @@ -0,0 +1,138 @@ +import presto + +import time +import gc + +from picographics import PicoGraphics, DISPLAY_PRESTO_FULL_RES +from picovector import PicoVector, Polygon, RegularPolygon, Rectangle, ANTIALIAS_X4, ANTIALIAS_X16 + +machine.freq(264000000) + +presto = presto.Presto(full_res=True) + +display = PicoGraphics(DISPLAY_PRESTO_FULL_RES) + +vector = PicoVector(display) +vector.set_antialiasing(ANTIALIAS_X4) + +RED = display.create_pen(200, 0, 0) +BLACK = display.create_pen(0, 0, 0) +GREY = display.create_pen(200, 200, 200) +WHITE = display.create_pen(255, 255, 255) + +""" +# Redefine colours for a Blue clock +RED = display.create_pen(200, 0, 0) +BLACK = display.create_pen(135, 159, 169) +GREY = display.create_pen(10, 40, 50) +WHITE = display.create_pen(14, 60, 76) +""" + +WIDTH, HEIGHT = display.get_bounds() + +hub = RegularPolygon(int(WIDTH / 2), int(HEIGHT / 2), 24, 5) + +face = RegularPolygon(int(WIDTH / 2), int(HEIGHT / 2), 48, int(HEIGHT / 2)) + +print(time.localtime()) + +last_second = None + +def fade_in(): + for j in range(101): + presto.set_backlight(j*0.01) + time.sleep_us(700) + +def fade_out(): + for j in range(101): + presto.set_backlight(1 - j*0.01) + time.sleep_us(700) + +while True: + t_start = time.ticks_ms() + year, month, day, hour, minute, second, _, _ = time.localtime() + + if last_second == second: + time.sleep_ms(10) + continue + + last_second = second + + fade_out() + + display.set_pen(0) + display.clear() + + display.set_pen(BLACK) + display.circle(int(WIDTH / 2), int(HEIGHT / 2), int(HEIGHT / 2)) + display.set_pen(WHITE) + display.circle(int(WIDTH / 2), int(HEIGHT / 2), int(HEIGHT / 2) - 4) + + display.set_pen(GREY) + + for a in range(60): + tick_mark = Rectangle(int(WIDTH / 2) - 3, 10, 6, int(HEIGHT / 48)) + vector.rotate(tick_mark, 360 / 60.0 * a, int(WIDTH / 2), int(HEIGHT / 2)) + vector.translate(tick_mark, 0, 2) + vector.draw(tick_mark) + + for a in range(12): + hour_mark = Rectangle(int(WIDTH / 2) - 5, 10, 10, int(HEIGHT / 10)) + vector.rotate(hour_mark, 360 / 12.0 * a, int(WIDTH / 2), int(HEIGHT / 2)) + vector.translate(hour_mark, 0, 2) + vector.draw(hour_mark) + + angle_second = second * 6 + second_hand_length = int(HEIGHT / 2) - int(HEIGHT / 8) + second_hand = Polygon((-2, -second_hand_length), (-2, int(HEIGHT / 8)), (2, int(HEIGHT / 8)), (2, -second_hand_length)) + vector.rotate(second_hand, angle_second, 0, 0) + vector.translate(second_hand, int(WIDTH / 2), int(HEIGHT / 2) + 5) + + angle_minute = minute * 6 + angle_minute += second / 10.0 + minute_hand_length = int(HEIGHT / 2) - int(HEIGHT / 24) + minute_hand = Polygon((-5, -minute_hand_length), (-10, int(HEIGHT / 16)), (10, int(HEIGHT / 16)), (5, -minute_hand_length)) + vector.rotate(minute_hand, angle_minute, 0, 0) + vector.translate(minute_hand, int(WIDTH / 2), int(HEIGHT / 2) + 5) + + angle_hour = (hour % 12) * 30 + angle_hour += minute / 2 + hour_hand_length = int(HEIGHT / 2) - int(HEIGHT / 8) + hour_hand = Polygon((-5, -hour_hand_length), (-10, int(HEIGHT / 16)), (10, int(HEIGHT / 16)), (5, -hour_hand_length)) + vector.rotate(hour_hand, angle_hour, 0, 0) + vector.translate(hour_hand, int(WIDTH / 2), int(HEIGHT / 2) + 5) + + display.set_pen(GREY) + + vector.draw(minute_hand) + vector.draw(hour_hand) + vector.draw(second_hand) + + display.set_pen(BLACK) + + for a in range(60): + tick_mark = Rectangle(int(WIDTH / 2) - 3, 10, 6, int(HEIGHT / 48)) + vector.rotate(tick_mark, 360 / 60.0 * a, int(WIDTH / 2), int(HEIGHT / 2)) + vector.draw(tick_mark) + + for a in range(12): + hour_mark = Rectangle(int(WIDTH / 2) - 5, 10, 10, int(HEIGHT / 10)) + vector.rotate(hour_mark, 360 / 12.0 * a, int(WIDTH / 2), int(HEIGHT / 2)) + vector.draw(hour_mark) + + vector.translate(minute_hand, 0, -5) + vector.translate(hour_hand, 0, -5) + vector.draw(minute_hand) + vector.draw(hour_hand) + + display.set_pen(RED) + vector.translate(second_hand, 0, -5) + vector.draw(second_hand) + vector.draw(hub) + + presto.update(display) + fade_in() + gc.collect() + + t_end = time.ticks_ms() + print(f"Took {t_end - t_start}ms") From 2d5e899fde914cd4a7063c52be386677e325cc3a Mon Sep 17 00:00:00 2001 From: Mike Bell Date: Sun, 17 Nov 2024 15:38:26 +0000 Subject: [PATCH 4/7] ST7701: Single program for full and half res. --- drivers/st7701/st7701.cpp | 32 +++++++++++------------------- drivers/st7701/st7701_parallel.pio | 16 +-------------- 2 files changed, 13 insertions(+), 35 deletions(-) diff --git a/drivers/st7701/st7701.cpp b/drivers/st7701/st7701.cpp index 81df60a..de68256 100644 --- a/drivers/st7701/st7701.cpp +++ b/drivers/st7701/st7701.cpp @@ -224,13 +224,7 @@ void ST7701::init_framebuffer() { st_pio = pio1; parallel_sm = pio_claim_unused_sm(st_pio, true); - if (width == 480) parallel_offset = pio_add_program(st_pio, &st7701_parallel_program); - else if (width == 240) parallel_offset = pio_add_program(st_pio, &st7701_parallel_double_program); - else { - printf("Unsupported width\n"); - return; - } - + parallel_offset = pio_add_program(st_pio, &st7701_parallel_program); if (height == 240) row_shift = 1; timing_sm = pio_claim_unused_sm(st_pio, true); @@ -268,9 +262,7 @@ void ST7701::init_framebuffer() { pio_sm_set_consecutive_pindirs(st_pio, parallel_sm, d0, 16, true); pio_sm_set_consecutive_pindirs(st_pio, parallel_sm, hsync, 4, true); - pio_sm_config c; - if (width == 480) c = st7701_parallel_program_get_default_config(parallel_offset); - else c = st7701_parallel_double_program_get_default_config(parallel_offset); + pio_sm_config c = st7701_parallel_program_get_default_config(parallel_offset); sm_config_set_out_pins(&c, d0, 16); sm_config_set_sideset_pins(&c, lcd_de); @@ -279,18 +271,18 @@ void ST7701::init_framebuffer() { sm_config_set_in_shift(&c, false, false, 32); // Determine clock divider - uint32_t max_pio_clk; - if ((intptr_t)framebuffer >= 0x14000000 || width <= 240) { - // Frame buffer in internal RAM or pixel doubling, go as fast as possible - max_pio_clk = 34 * MHZ; + uint32_t max_pio_clk = 34 * MHZ; + const uint32_t sys_clk_hz = clock_get_hz(clk_sys); + uint32_t clk_div = (sys_clk_hz + max_pio_clk - 1) / max_pio_clk; + if (width == 480) { + // Parallel output SM must run at double the rate of the timing SM for full res + if (clk_div & 1) clk_div += 1; + sm_config_set_clkdiv(&c, clk_div >> 1); } - else { - // Frame buffer in PSRAM and not pixel doubling, slow down a bit - max_pio_clk = 25 * MHZ; + else + { + sm_config_set_clkdiv(&c, clk_div); } - const uint32_t sys_clk_hz = clock_get_hz(clk_sys); - const uint32_t clk_div = (sys_clk_hz + max_pio_clk - 1) / max_pio_clk; - sm_config_set_clkdiv(&c, clk_div); pio_sm_init(st_pio, parallel_sm, parallel_offset, &c); pio_sm_exec(st_pio, parallel_sm, pio_encode_out(pio_y, 32)); diff --git a/drivers/st7701/st7701_parallel.pio b/drivers/st7701/st7701_parallel.pio index 2836f72..3b1f510 100644 --- a/drivers/st7701/st7701_parallel.pio +++ b/drivers/st7701/st7701_parallel.pio @@ -1,22 +1,8 @@ -; Output 16 bit parallel RGB565 data every clock +; Output 16 bit parallel, bit reversed, RGB565 data every 4th clock ; Wait for irq 4 from the timing SM between each row ; Side-set is data enable .program st7701_parallel -.side_set 1 - - mov pins, null side 0 - wait 1 irq 4 side 0 -.wrap_target - out isr, 32 side 1 - mov pins, ::isr side 1 - in null, 16 side 1 - mov pins, ::isr side 1 -.wrap - -; Output 16 bit parallel RGB565 data every other clock - -.program st7701_parallel_double .side_set 1 .wrap_target From 4c359639df585141fb9bf2e10a6e5562cfb4d86e Mon Sep 17 00:00:00 2001 From: Mike Bell Date: Sun, 17 Nov 2024 15:38:26 +0000 Subject: [PATCH 5/7] PicoGraphics style update instead of buffer flip. --- drivers/st7701/st7701.cpp | 46 ++++++++++++------------------------- drivers/st7701/st7701.hpp | 2 +- modules/c/presto/presto.c | 2 ++ modules/c/presto/presto.cpp | 42 +++++++++++++++++++++++++-------- modules/c/presto/presto.h | 1 + 5 files changed, 51 insertions(+), 42 deletions(-) diff --git a/drivers/st7701/st7701.cpp b/drivers/st7701/st7701.cpp index de68256..1b26cb8 100644 --- a/drivers/st7701/st7701.cpp +++ b/drivers/st7701/st7701.cpp @@ -171,8 +171,6 @@ void ST7701::start_frame_xfer() if (next_framebuffer) { framebuffer = next_framebuffer; next_framebuffer = nullptr; - - init_framebuffer(); } next_line_addr = 0; @@ -192,19 +190,6 @@ void ST7701::start_frame_xfer() __sev(); } -void ST7701::init_framebuffer() { - // If framebuffer not in cache XIP memory, do nothing. - if ((intptr_t)framebuffer >= 0x14000000) return; - - // Flush the frame buffer from cache - uint8_t* framebuffer_maintenance = (uint8_t*)framebuffer + 0x8000000; - for (int i = 3; i < width*height << 1; i += 8) - framebuffer_maintenance[i] = 0; // Clean - - // Switch to uncached reads - framebuffer += 0x2000000; -} - ST7701::ST7701(uint16_t width, uint16_t height, Rotation rotation, SPIPins control_pins, uint16_t* framebuffer, uint d0, uint hsync, uint vsync, uint lcd_de, uint lcd_dot_clk) : DisplayDriver(width, height, rotation), @@ -219,8 +204,6 @@ void ST7701::init_framebuffer() { void ST7701::init() { irq_handler_t current = nullptr; - init_framebuffer(); - st_pio = pio1; parallel_sm = pio_claim_unused_sm(st_pio, true); @@ -511,23 +494,24 @@ void ST7701::init_framebuffer() { } void ST7701::update(PicoGraphics *graphics) { - //uint8_t cmd = reg::RAMWR; - - // TODO: Where's my buffer at? Where's my buffer at? - // I left it out back in the PSRAM chip, y'all - // And now I cannot find it even though it's 60k tall - - if(graphics->pen_type == PicoGraphics::PEN_RGB565) { // Display buffer is screen native - // command(cmd, width * height * sizeof(uint16_t), (const char*)graphics->frame_buffer); + if(graphics->pen_type == PicoGraphics::PEN_RGB565 && graphics->layers == 1) { // Display buffer is screen native + memcpy(framebuffer, graphics->frame_buffer, width * height * sizeof(uint16_t)); } else { - /*graphics->frame_convert(PicoGraphics::PEN_RGB565, [this](void *data, size_t length) { + uint8_t* frame_ptr = (uint8_t*)framebuffer; + graphics->frame_convert(PicoGraphics::PEN_RGB565, [this, &frame_ptr](void *data, size_t length) { if (length > 0) { - write_blocking_dma((const uint8_t*)data, length); - } - else { - dma_channel_wait_for_finish_blocking(st_dma); + memcpy(frame_ptr, data, length); + frame_ptr += length; } - });*/ + }); + } + } + + void ST7701::partial_update(PicoGraphics *graphics, Rect region) { + if(graphics->pen_type == PicoGraphics::PEN_RGB565 && graphics->layers == 1) { // Display buffer is screen native + for (int y = region.y; y < region.y + region.h; ++y) { + memcpy(&framebuffer[y * width + region.x], (uint16_t*)graphics->frame_buffer + y * width + region.x, region.w * sizeof(uint16_t)); + } } } diff --git a/drivers/st7701/st7701.hpp b/drivers/st7701/st7701.hpp index 622a8ef..531b340 100644 --- a/drivers/st7701/st7701.hpp +++ b/drivers/st7701/st7701.hpp @@ -57,6 +57,7 @@ namespace pimoroni { void init(); void cleanup() override; void update(PicoGraphics *graphics) override; + void partial_update(PicoGraphics *display, Rect region) override; void set_backlight(uint8_t brightness) override; void set_framebuffer(uint16_t* next_fb) { @@ -78,7 +79,6 @@ namespace pimoroni { void start_line_xfer(); void start_frame_xfer(); - void init_framebuffer(); // Timing status uint16_t timing_row = 0; diff --git a/modules/c/presto/presto.c b/modules/c/presto/presto.c index 8cfbc3b..ec63546 100644 --- a/modules/c/presto/presto.c +++ b/modules/c/presto/presto.c @@ -5,6 +5,7 @@ MP_DEFINE_CONST_FUN_OBJ_1(Presto___del___obj, Presto___del__); MP_DEFINE_CONST_FUN_OBJ_2(Presto_update_obj, Presto_update); +MP_DEFINE_CONST_FUN_OBJ_KW(Presto_partial_update_obj, 5, Presto_partial_update); MP_DEFINE_CONST_FUN_OBJ_2(Presto_set_backlight_obj, Presto_set_backlight); /***** Binding of Methods *****/ @@ -12,6 +13,7 @@ MP_DEFINE_CONST_FUN_OBJ_2(Presto_set_backlight_obj, Presto_set_backlight); static const mp_rom_map_elem_t Presto_locals_dict_table[] = { { MP_ROM_QSTR(MP_QSTR___del__), MP_ROM_PTR(&Presto___del___obj) }, { MP_ROM_QSTR(MP_QSTR_update), MP_ROM_PTR(&Presto_update_obj) }, + { MP_ROM_QSTR(MP_QSTR_partial_update), MP_ROM_PTR(&Presto_partial_update_obj) }, { MP_ROM_QSTR(MP_QSTR_set_backlight), MP_ROM_PTR(&Presto_set_backlight_obj) }, { MP_ROM_QSTR(MP_QSTR_WIDTH), MP_ROM_INT(WIDTH/2) }, diff --git a/modules/c/presto/presto.cpp b/modules/c/presto/presto.cpp index 74e2dc3..b6b221c 100644 --- a/modules/c/presto/presto.cpp +++ b/modules/c/presto/presto.cpp @@ -21,6 +21,7 @@ extern "C" { // MicroPython's GC heap will automatically resize, so we should just // statically allocate these in C++ to avoid fragmentation. __attribute__((section(".uninitialized_data"))) static uint16_t presto_buffer[WIDTH * HEIGHT] = {0}; +__attribute__((section(".psram_data"))) static uint16_t gfx_buffer[WIDTH * HEIGHT] = {0}; void __printf_debug_flush() { for(auto i = 0u; i < 10; i++) { @@ -44,7 +45,6 @@ void presto_debug(const char *fmt, ...) { typedef struct _Presto_obj_t { mp_obj_base_t base; ST7701* presto; - uint16_t* next_fb; uint16_t* curr_fb; uint16_t width; uint16_t height; @@ -74,7 +74,7 @@ void presto_core1_entry() { multicore_fifo_push_blocking(0); } -#define stack_size 4096u +#define stack_size 256u static uint32_t core1_stack[stack_size] = {0}; mp_obj_t Presto_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) { @@ -93,23 +93,22 @@ mp_obj_t Presto_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, self = mp_obj_malloc_with_finaliser(_Presto_obj_t, &Presto_type); presto_debug("set fb pointers\n"); - self->curr_fb = presto_buffer; - self->next_fb = presto_buffer; if (!args[ARG_full_res].u_bool) { + self->curr_fb = presto_buffer + (WIDTH * HEIGHT) / 4; self->width = WIDTH / 2; self->height = HEIGHT / 2; - self->next_fb += (WIDTH * HEIGHT) / 4; } else { self->width = WIDTH; self->height = HEIGHT; + self->curr_fb = gfx_buffer; } presto_debug("m_new_class(ST7701...\n"); ST7701 *presto = m_new_class(ST7701, self->width, self->height, ROTATE_0, SPIPins{spi1, LCD_CS, LCD_CLK, LCD_DAT, PIN_UNUSED, LCD_DC, BACKLIGHT}, - self->next_fb, + presto_buffer, LCD_D0); self->presto = presto; @@ -160,11 +159,34 @@ extern mp_obj_t Presto_update(mp_obj_t self_in, mp_obj_t graphics_in) { _Presto_obj_t *self = MP_OBJ_TO_PTR2(self_in, _Presto_obj_t); ModPicoGraphics_obj_t *picographics = MP_OBJ_TO_PTR2(graphics_in, ModPicoGraphics_obj_t); - self->presto->set_framebuffer(self->next_fb); - std::swap(self->next_fb, self->curr_fb); - picographics->graphics->set_framebuffer((void *)self->next_fb); + self->presto->update(picographics->graphics); - self->presto->wait_for_vsync(); + return mp_const_none; +} + +extern mp_obj_t Presto_partial_update(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + enum { ARG_self, ARG_graphics, ARG_x, ARG_y, ARG_w, ARG_h }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ }, + { MP_QSTR_graphics, MP_ARG_REQUIRED | MP_ARG_OBJ }, + { MP_QSTR_x, MP_ARG_REQUIRED | MP_ARG_INT }, + { MP_QSTR_y, MP_ARG_REQUIRED | MP_ARG_INT }, + { MP_QSTR_w, MP_ARG_REQUIRED | MP_ARG_INT }, + { MP_QSTR_h, MP_ARG_REQUIRED | MP_ARG_INT } + }; + + // Parse args. + 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); + + _Presto_obj_t *self = MP_OBJ_TO_PTR2(args[ARG_self].u_obj, _Presto_obj_t); + ModPicoGraphics_obj_t *picographics = MP_OBJ_TO_PTR2(args[ARG_graphics].u_obj, ModPicoGraphics_obj_t); + int x = args[ARG_x].u_int; + int y = args[ARG_y].u_int; + int w = args[ARG_w].u_int; + int h = args[ARG_h].u_int; + + self->presto->partial_update(picographics->graphics, {x, y, w, h}); return mp_const_none; } diff --git a/modules/c/presto/presto.h b/modules/c/presto/presto.h index 3c72a98..0896167 100644 --- a/modules/c/presto/presto.h +++ b/modules/c/presto/presto.h @@ -18,6 +18,7 @@ extern const mp_obj_type_t Presto_type; /***** Extern of Class Methods *****/ extern mp_obj_t Presto_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args); extern mp_obj_t Presto_update(mp_obj_t self_in, mp_obj_t graphics_in); +extern mp_obj_t Presto_partial_update(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args); extern mp_int_t Presto_get_framebuffer(mp_obj_t self_in, mp_buffer_info_t *bufinfo, mp_uint_t flags); extern mp_obj_t Presto_set_backlight(mp_obj_t self_in, mp_obj_t brightness); extern mp_obj_t Presto___del__(mp_obj_t self_in); \ No newline at end of file From 612cdc3eb32154e37c3edc4652e7ec8e532eb5e3 Mon Sep 17 00:00:00 2001 From: Mike Bell Date: Sun, 17 Nov 2024 15:38:26 +0000 Subject: [PATCH 6/7] MicroPython: Switch to new style of enabling PSRAM. --- presto/mpconfigboard.cmake | 3 +++ presto/mpconfigboard.h | 1 - 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/presto/mpconfigboard.cmake b/presto/mpconfigboard.cmake index 95c568d..ad4454d 100644 --- a/presto/mpconfigboard.cmake +++ b/presto/mpconfigboard.cmake @@ -13,6 +13,9 @@ set(MICROPY_FROZEN_MANIFEST ${MICROPY_BOARD_DIR}/manifest.py) # there needs to be some RAM reserved for the C heap set(MICROPY_C_HEAP_SIZE 4096) +# Enable PSRAM +set(MICROPY_HW_ENABLE_PSRAM ON) + # Links micropy_lib_lwip and sets MICROPY_PY_LWIP = 1 # Picked up and expanded upon in mpconfigboard.h set(MICROPY_PY_LWIP ON) diff --git a/presto/mpconfigboard.h b/presto/mpconfigboard.h index c7c5f25..29858de 100644 --- a/presto/mpconfigboard.h +++ b/presto/mpconfigboard.h @@ -30,7 +30,6 @@ int mp_hal_is_pin_reserved(int n); // Alias the chip select pin specified by presto.h #define MICROPY_HW_PSRAM_CS_PIN PIMORONI_PRESTO_PSRAM_CS_PIN -#define MICROPY_HW_ENABLE_PSRAM (1) #define MICROPY_PY_THREAD (0) #define MICROPY_GC_SPLIT_HEAP (0) From d9577e6cd20cae621fc820bfb8e18c53dd560e52 Mon Sep 17 00:00:00 2001 From: Mike Bell Date: Sun, 17 Nov 2024 15:38:26 +0000 Subject: [PATCH 7/7] Improve overclockability. --- presto/presto.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/presto/presto.h b/presto/presto.h index 864b5d8..78acd56 100644 --- a/presto/presto.h +++ b/presto/presto.h @@ -120,4 +120,7 @@ #define PICO_RP2350_A2_SUPPORTED 1 #endif +// Increase the clock divider to allow additional overclocking headroom +#define CYW43_PIO_CLOCK_DIV_INT 4 + #endif