diff --git a/drivers/st7701/st7701.cpp b/drivers/st7701/st7701.cpp index 4e444d7..ebf47ba 100644 --- a/drivers/st7701/st7701.cpp +++ b/drivers/st7701/st7701.cpp @@ -1,3 +1,10 @@ +// ST7701 driver for Pimoroni Presto 480x480 18bpp display +// +// This implementation is mainly due to Michael Bell and Phil Howard (Gadgetoid). +// +// Big thanks to Dmitry GR for the inspiration and demo implementation of the 8bpp +// palette mode, upon which the same functionality here was based. + #include "st7701.hpp" #include @@ -10,6 +17,7 @@ #ifndef NO_QSTR #include "st7701_parallel.pio.h" #include "st7701_timing.pio.h" +#include "st7701_palette.pio.h" #endif namespace pimoroni { @@ -161,6 +169,7 @@ void __no_inline_not_in_flash_func(ST7701::start_line_xfer()) ++display_row; if (display_row == DISPLAY_HEIGHT) next_line_addr = 0; + else if (palette) next_line_addr = &framebuffer[(width >> 1) * (display_row >> row_shift)]; else next_line_addr = &framebuffer[width * (display_row >> row_shift)]; } @@ -190,13 +199,14 @@ void ST7701::start_frame_xfer() __sev(); } - ST7701::ST7701(uint16_t width, uint16_t height, Rotation rotation, SPIPins control_pins, uint16_t* framebuffer, + ST7701::ST7701(uint16_t width, uint16_t height, Rotation rotation, SPIPins control_pins, uint16_t* framebuffer, uint32_t* palette, uint d0, uint hsync, uint vsync, uint lcd_de, uint lcd_dot_clk) : DisplayDriver(width, height, rotation), spi(control_pins.spi), spi_cs(control_pins.cs), spi_sck(control_pins.sck), spi_dat(control_pins.mosi), lcd_bl(control_pins.bl), d0(d0), hsync(hsync), vsync(vsync), lcd_de(lcd_de), lcd_dot_clk(lcd_dot_clk), - framebuffer(framebuffer) + framebuffer(framebuffer), + palette(palette) { st7701_inst = this; } @@ -207,12 +217,23 @@ void ST7701::start_frame_xfer() st_pio = pio1; parallel_sm = pio_claim_unused_sm(st_pio, true); - 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); timing_offset = pio_add_program(st_pio, &st7701_timing_program); + + if (palette) { + parallel_offset = pio_add_program(st_pio, &st7701_parallel_18bpp_program); + + palette_sm = pio_claim_unused_sm(st_pio, true); + palette_offset = pio_add_program(st_pio, &st7701_palette_program); + } + else { + parallel_offset = pio_add_program(st_pio, &st7701_parallel_program); + } + + row_shift = 0; + if (height == 240) row_shift = 1; + spi_init(spi, SPI_BAUD); gpio_set_function(spi_cs, GPIO_FUNC_SIO); gpio_set_dir(spi_cs, GPIO_OUT); @@ -233,21 +254,27 @@ void ST7701::start_frame_xfer() pio_gpio_init(st_pio, lcd_de); pio_gpio_init(st_pio, lcd_dot_clk); - for(auto i = 0u; i < 16; i++) { + const uint num_data_pins = palette ? 18 : 16; + + for(auto i = 0u; i < num_data_pins; i++) { pio_gpio_init(st_pio, d0 + i); } - for(auto i = 16u; i < 18; i++) { - gpio_init(d0 + i); - gpio_set_dir(d0 + i, GPIO_OUT); - gpio_put(d0 + i, false); + + if (!palette) { + for(auto i = 16u; i < 18; i++) { + gpio_init(d0 + i); + gpio_set_dir(d0 + i, GPIO_OUT); + gpio_put(d0 + i, false); + } } - pio_sm_set_consecutive_pindirs(st_pio, parallel_sm, d0, 16, true); + pio_sm_set_consecutive_pindirs(st_pio, parallel_sm, d0, num_data_pins, true); pio_sm_set_consecutive_pindirs(st_pio, parallel_sm, hsync, 4, true); + + pio_sm_config c = palette ? st7701_parallel_18bpp_program_get_default_config(parallel_offset) : + st7701_parallel_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_out_pins(&c, d0, num_data_pins); sm_config_set_sideset_pins(&c, lcd_de); sm_config_set_fifo_join(&c, PIO_FIFO_JOIN_TX); sm_config_set_out_shift(&c, true, true, 32); @@ -257,6 +284,11 @@ void ST7701::start_frame_xfer() 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 (palette && width == 480) { + // Minimum clock divisor of 8 to ensure there is time for the palette decode + if (clk_div < 8) clk_div = 8; + } + 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; @@ -266,7 +298,7 @@ void ST7701::start_frame_xfer() { 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)); pio_sm_put(st_pio, parallel_sm, (width >> 1) - 1); @@ -283,20 +315,63 @@ void ST7701::start_frame_xfer() pio_sm_init(st_pio, timing_sm, timing_offset, &c); pio_sm_set_enabled(st_pio, timing_sm, true); + if (palette) { + c = st7701_palette_program_get_default_config(palette_offset); + sm_config_set_out_shift(&c, false, true, 32); + sm_config_set_in_shift(&c, true, true, 30); + + pio_sm_init(st_pio, palette_sm, palette_offset, &c); + pio_sm_exec(st_pio, palette_sm, pio_encode_out(pio_x, 32)); + pio_sm_put(st_pio, palette_sm, ((uintptr_t)palette) >> 10); + pio_sm_set_enabled(st_pio, palette_sm, true); + } + st_dma = dma_claim_unused_channel(true); st_dma2 = dma_claim_unused_channel(true); - dma_channel_config config = dma_channel_get_default_config(st_dma); - channel_config_set_transfer_data_size(&config, DMA_SIZE_32); - channel_config_set_dreq(&config, pio_get_dreq(st_pio, parallel_sm, true)); - channel_config_set_bswap(&config, true); - channel_config_set_chain_to(&config, st_dma2); - dma_channel_configure(st_dma, &config, &st_pio->txf[parallel_sm], nullptr, width >> 1, false); - - config = dma_channel_get_default_config(st_dma2); - channel_config_set_transfer_data_size(&config, DMA_SIZE_32); - channel_config_set_read_increment(&config, false); - dma_channel_configure(st_dma2, &config, &dma_hw->ch[st_dma].al3_read_addr_trig, &next_line_addr, 1, false); + if (!palette) { + // Regular RGB565 framebuffer + dma_channel_config config = dma_channel_get_default_config(st_dma); + channel_config_set_transfer_data_size(&config, DMA_SIZE_32); + channel_config_set_dreq(&config, pio_get_dreq(st_pio, parallel_sm, true)); + channel_config_set_bswap(&config, true); + channel_config_set_chain_to(&config, st_dma2); + dma_channel_configure(st_dma, &config, &st_pio->txf[parallel_sm], nullptr, width >> 1, false); + + config = dma_channel_get_default_config(st_dma2); + channel_config_set_transfer_data_size(&config, DMA_SIZE_32); + channel_config_set_read_increment(&config, false); + dma_channel_configure(st_dma2, &config, &dma_hw->ch[st_dma].al3_read_addr_trig, &next_line_addr, 1, false); + } + else { + st_dma3 = dma_claim_unused_channel(true); + st_dma4 = dma_claim_unused_channel(true); + + dma_channel_config config = dma_channel_get_default_config(st_dma); + channel_config_set_transfer_data_size(&config, DMA_SIZE_32); + channel_config_set_dreq(&config, pio_get_dreq(st_pio, palette_sm, true)); + channel_config_set_bswap(&config, true); + channel_config_set_chain_to(&config, st_dma2); + dma_channel_configure(st_dma, &config, &st_pio->txf[palette_sm], nullptr, width >> 2, false); + + config = dma_channel_get_default_config(st_dma2); + channel_config_set_transfer_data_size(&config, DMA_SIZE_32); + channel_config_set_read_increment(&config, false); + dma_channel_configure(st_dma2, &config, &dma_hw->ch[st_dma].al3_read_addr_trig, &next_line_addr, 1, false); + + config = dma_channel_get_default_config(st_dma3); + channel_config_set_transfer_data_size(&config, DMA_SIZE_32); + channel_config_set_dreq(&config, pio_get_dreq(st_pio, parallel_sm, true)); + channel_config_set_read_increment(&config, false); + channel_config_set_chain_to(&config, st_dma4); + dma_channel_configure(st_dma3, &config, &st_pio->txf[parallel_sm], nullptr, 1, false); + + config = dma_channel_get_default_config(st_dma4); + channel_config_set_transfer_data_size(&config, DMA_SIZE_32); + channel_config_set_dreq(&config, pio_get_dreq(st_pio, palette_sm, false)); + channel_config_set_read_increment(&config, false); + dma_channel_configure(st_dma4, &config, &dma_hw->ch[st_dma3].al3_read_addr_trig, &st_pio->rxf[palette_sm], 1, true); + } printf("Begin SPI setup\n"); @@ -430,6 +505,12 @@ void ST7701::start_frame_xfer() if(dma_channel_is_claimed(st_dma2)) { dma_channel_unclaim(st_dma2); } + if(dma_channel_is_claimed(st_dma3)) { + dma_channel_unclaim(st_dma3); + } + if(dma_channel_is_claimed(st_dma4)) { + dma_channel_unclaim(st_dma4); + } if(pio_sm_is_claimed(st_pio, parallel_sm)) { pio_sm_set_enabled(st_pio, parallel_sm, false); @@ -443,6 +524,12 @@ void ST7701::start_frame_xfer() pio_sm_unclaim(st_pio, timing_sm); } + if(pio_sm_is_claimed(st_pio, palette_sm)) { + pio_sm_set_enabled(st_pio, palette_sm, false); + pio_sm_clear_fifos(st_pio, palette_sm); + pio_sm_unclaim(st_pio, palette_sm); + } + pio_clear_instruction_memory(st_pio); } @@ -461,23 +548,6 @@ void ST7701::start_frame_xfer() command(reg::MADCTL, 1, (char *)&madctl); } - void ST7701::write_blocking_dma(const uint8_t *src, size_t len) { - while (dma_channel_is_busy(st_dma)) - ; - dma_channel_set_trans_count(st_dma, len, false); - dma_channel_set_read_addr(st_dma, src, true); - } - - void ST7701::write_blocking_parallel(const uint8_t *src, size_t len) { - write_blocking_dma(src, len); - dma_channel_wait_for_finish_blocking(st_dma); - - // This may cause a race between PIO and the - // subsequent chipselect deassert for the last pixel - while(!pio_sm_is_tx_fifo_empty(st_pio, parallel_sm)) - ; - } - void ST7701::command(uint8_t command, size_t len, const char *data) { static uint16_t _data[20] = {0}; gpio_put(spi_cs, 0); @@ -500,7 +570,7 @@ void ST7701::start_frame_xfer() } void ST7701::update(PicoGraphics *graphics) { - if(graphics->pen_type == PicoGraphics::PEN_RGB565) { // Display buffer is screen native + if(graphics->pen_type == PicoGraphics::PEN_RGB565 && !palette) { // Display buffer is screen native if (graphics->frame_buffer == framebuffer) { // Nothing to do return; @@ -542,6 +612,33 @@ void ST7701::start_frame_xfer() } } } + } else if (graphics->pen_type == PicoGraphics::PEN_P8 && palette) { + wait_for_vsync(); + PicoGraphics_PenP8* pen8 = static_cast(graphics); + RGB* palette = pen8->get_palette(); + for (int i = 0; i < 256; ++i) { + set_palette_colour(i, palette[i]); + } + if (graphics->layers == 1) { + memcpy(framebuffer, graphics->frame_buffer, width * height); + } + else { + uint8_t* dst = (uint8_t*)framebuffer; + const uint8_t* end = dst + width * height; + uint8_t* src = (uint8_t*)graphics->frame_buffer; + const size_t layer_offset = width * height; + const int top_layer_idx = graphics->layers - 1; + + while (dst != end) { + uint8_t colour = 0; + for (int layer = top_layer_idx; layer >= 0; --layer) { + colour = *(src + layer * layer_offset); + if (colour) break; + } + *dst++ = colour; + ++src; + } + } } else { uint8_t* frame_ptr = (uint8_t*)framebuffer; graphics->frame_convert(PicoGraphics::PEN_RGB565, [this, &frame_ptr](void *data, size_t length) { @@ -554,11 +651,17 @@ void ST7701::start_frame_xfer() } void ST7701::partial_update(PicoGraphics *graphics, Rect region) { - if(graphics->pen_type == PicoGraphics::PEN_RGB565 && graphics->layers == 1) { // Display buffer is screen native + if (graphics->pen_type == PicoGraphics::PEN_RGB565 && !palette && 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)); } } + else if (graphics->pen_type == PicoGraphics::PEN_P8 && palette && graphics->layers == 1) { + uint8_t* fb8 = (uint8_t*)framebuffer; + for (int y = region.y; y < region.y + region.h; ++y) { + memcpy(&fb8[y * width + region.x], (uint8_t*)graphics->frame_buffer + y * width + region.x, region.w * sizeof(uint8_t)); + } + } } void ST7701::set_backlight(uint8_t brightness) { @@ -570,6 +673,32 @@ void ST7701::start_frame_xfer() pwm_set_gpio_level(lcd_bl, value); } + void ST7701::set_palette_colour(uint8_t entry, RGB888 colour) { + if (!palette) return; + + // Note bit reversal is done by PIO. + uint32_t encoded_colour = + ((colour << 8) & 0xF8000000) | // R + ((colour << 11) & 0x07E00000) | // G + ((colour << 13) & 0x001F8000) | // B + ((colour >> 4) & 0x00004000); // Low bit of R + + palette[entry] = encoded_colour; + } + + void ST7701::set_palette_colour(uint8_t entry, const RGB& colour) { + if (!palette) return; + + // Note bit reversal is done by PIO. + uint32_t encoded_colour = + ((colour.r << 24) & 0xF8000000) | // R + ((colour.g << 19) & 0x07E00000) | // G + ((colour.b << 13) & 0x001F8000) | // B + ((colour.r << 12) & 0x00004000); // Low bit of R + + palette[entry] = encoded_colour; + } + void __no_inline_not_in_flash_func(ST7701::wait_for_vsync()) { waiting_for_vsync = true; while (waiting_for_vsync) __wfe(); diff --git a/drivers/st7701/st7701.hpp b/drivers/st7701/st7701.hpp index 455020b..35f3962 100644 --- a/drivers/st7701/st7701.hpp +++ b/drivers/st7701/st7701.hpp @@ -30,11 +30,15 @@ namespace pimoroni { uint lcd_bl; uint parallel_sm; uint timing_sm; + uint palette_sm; PIO st_pio; uint parallel_offset; uint timing_offset; + uint palette_offset; uint st_dma; uint st_dma2; + int st_dma3 = -1; + int st_dma4 = -1; uint d0 = 1; // First pin of 18-bit parallel interface uint hsync = 19; @@ -47,7 +51,7 @@ namespace pimoroni { public: // Parallel init - ST7701(uint16_t width, uint16_t height, Rotation rotation, SPIPins control_pins, uint16_t* framebuffer, + ST7701(uint16_t width, uint16_t height, Rotation rotation, SPIPins control_pins, uint16_t* framebuffer, uint32_t* palette = nullptr, uint d0=1, uint hsync=19, uint vsync=20, uint lcd_de = 21, uint lcd_dot_clk = 22); void init(); @@ -56,6 +60,13 @@ namespace pimoroni { void partial_update(PicoGraphics *display, Rect region) override; void set_backlight(uint8_t brightness) override; + void set_palette_colour(uint8_t entry, RGB888 colour); + void set_palette_colour(uint8_t entry, const RGB& colour); + + // The format is an 18-bit value: RGB566, followed by the final bit of red. + // It is MSB aligned, i.e. the top bit of red is in the MSB. + uint32_t get_encoded_palette_entry(uint8_t entry) const { return palette[entry]; } + void set_framebuffer(uint16_t* next_fb) { next_framebuffer = next_fb; } @@ -69,8 +80,6 @@ namespace pimoroni { private: void common_init(); void configure_display(Rotation rotate); - void write_blocking_dma(const uint8_t *src, size_t len); - void write_blocking_parallel(const uint8_t *src, size_t len); void command(uint8_t command, size_t len = 0, const char *data = NULL); void start_line_xfer(); @@ -84,6 +93,8 @@ namespace pimoroni { uint16_t* framebuffer; uint16_t* next_framebuffer = nullptr; + uint32_t* palette = nullptr; + uint16_t* next_line_addr; int display_row = 0; int row_shift = 0; diff --git a/drivers/st7701/st7701_palette.pio b/drivers/st7701/st7701_palette.pio new file mode 100644 index 0000000..5aae322 --- /dev/null +++ b/drivers/st7701/st7701_palette.pio @@ -0,0 +1,12 @@ +; Program for decoding a 256 colour palette. Takes an 8-bit index, and creates +; the memory address of the palette entry. +; +; Thanks to DmitryGR for the idea, and implementation this is based on. + +.program st7701_palette + +.wrap_target + out y, 8 + in y, 8 + in x, 22 +.wrap diff --git a/drivers/st7701/st7701_parallel.pio b/drivers/st7701/st7701_parallel.pio index d5453af..2e1189c 100644 --- a/drivers/st7701/st7701_parallel.pio +++ b/drivers/st7701/st7701_parallel.pio @@ -16,3 +16,18 @@ loop: jmp x-- loop side 1 mov pins, null side 1 .wrap + +.program st7701_parallel_18bpp +.side_set 1 + +.wrap_target + mov x, y side 0 ; y needs to be set to (width/2)-1 at init time + wait 1 irq 4 side 0 ; wait for the irq from the timing SM +loop: + out isr, 32 side 1 + mov pins, ::isr side 1 [1] + out isr, 32 side 1 [1] + mov pins, ::isr side 1 [1] + jmp x-- loop side 1 + mov pins, null side 1 +.wrap diff --git a/drivers/st7701/st7701_presto.cmake b/drivers/st7701/st7701_presto.cmake index c4fe455..d7548bd 100644 --- a/drivers/st7701/st7701_presto.cmake +++ b/drivers/st7701/st7701_presto.cmake @@ -5,6 +5,7 @@ target_sources(st7701_presto INTERFACE pico_generate_pio_header(st7701_presto ${CMAKE_CURRENT_LIST_DIR}/st7701_parallel.pio) pico_generate_pio_header(st7701_presto ${CMAKE_CURRENT_LIST_DIR}/st7701_timing.pio) +pico_generate_pio_header(st7701_presto ${CMAKE_CURRENT_LIST_DIR}/st7701_palette.pio) target_include_directories(st7701_presto INTERFACE ${CMAKE_CURRENT_LIST_DIR}) diff --git a/modules/c/presto/micropython.cmake b/modules/c/presto/micropython.cmake index ab43acd..3b04a4e 100644 --- a/modules/c/presto/micropython.cmake +++ b/modules/c/presto/micropython.cmake @@ -6,10 +6,10 @@ target_sources(usermod_presto INTERFACE ${CMAKE_CURRENT_LIST_DIR}/presto.c ${CMAKE_CURRENT_LIST_DIR}/presto.cpp ${REPO_ROOT}/drivers/st7701/st7701.cpp - ${PIMORONI_PICO_PATH}/libraries/pico_graphics/pico_graphics_pen_rgb565.cpp ) pico_generate_pio_header(usermod_presto ${REPO_ROOT}/drivers/st7701/st7701_parallel.pio) pico_generate_pio_header(usermod_presto ${REPO_ROOT}/drivers/st7701/st7701_timing.pio) +pico_generate_pio_header(usermod_presto ${REPO_ROOT}/drivers/st7701/st7701_palette.pio) set_source_files_properties(${REPO_ROOT}/drivers/st7701/st7701.cpp PROPERTIES COMPILE_OPTIONS "-O2;-fgcse-after-reload;-floop-interchange;-fpeel-loops;-fpredictive-commoning;-fsplit-paths;-ftree-loop-distribute-patterns;-ftree-loop-distribution;-ftree-vectorize;-ftree-partial-pre;-funswitch-loops") diff --git a/modules/c/presto/presto.cpp b/modules/c/presto/presto.cpp index bd12300..7a73e61 100644 --- a/modules/c/presto/presto.cpp +++ b/modules/c/presto/presto.cpp @@ -21,7 +21,8 @@ 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(".uninitialized_data"))) static uint16_t presto_buffer[WIDTH * HEIGHT]; +__attribute__((section(".uninitialized_data"), aligned(1024))) static uint32_t presto_palette[256]; void __printf_debug_flush() { for(auto i = 0u; i < 10; i++) { @@ -51,6 +52,7 @@ typedef struct _Presto_obj_t { ST7701* presto; uint16_t width; uint16_t height; + bool using_palette; volatile bool exit_core1; // Automatic ambient backlight control @@ -93,13 +95,28 @@ static void __no_inline_not_in_flash_func(update_backlight_leds)() { uint32_t r = presto_obj->led_values[i].r; uint32_t g = presto_obj->led_values[i].g; uint32_t b = presto_obj->led_values[i].b; - for (int y = 0; y < SAMPLE_RANGE; ++y) { - uint16_t* ptr = &presto_buffer[(led_sample_locations[i].y + y) * presto_obj->width + led_sample_locations[i].x]; - for (int x = 0; x < SAMPLE_RANGE; ++x) { - uint16_t sample = __builtin_bswap16(*ptr++); - r += (sample >> 8) & 0xF8; - g += (sample >> 3) & 0xFC; - b += (sample << 3) & 0xF8; + + if (presto_obj->using_palette) { + for (int y = 0; y < SAMPLE_RANGE; ++y) { + uint8_t* ptr = (uint8_t*)presto_buffer; + ptr += (led_sample_locations[i].y + y) * presto_obj->width + led_sample_locations[i].x; + for (int x = 0; x < SAMPLE_RANGE; ++x) { + uint16_t sample = presto_obj->presto->get_encoded_palette_entry(*ptr++) >> 16; + r += (sample >> 8) & 0xF8; + g += (sample >> 3) & 0xFC; + b += (sample << 3) & 0xF8; + } + } + } + else { + for (int y = 0; y < SAMPLE_RANGE; ++y) { + uint16_t* ptr = &presto_buffer[(led_sample_locations[i].y + y) * presto_obj->width + led_sample_locations[i].x]; + for (int x = 0; x < SAMPLE_RANGE; ++x) { + uint16_t sample = __builtin_bswap16(*ptr++); + r += (sample >> 8) & 0xF8; + g += (sample >> 3) & 0xFC; + b += (sample << 3) & 0xF8; + } } } presto_obj->led_values[i].r = r; @@ -162,9 +179,10 @@ 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_full_res }; + enum { ARG_full_res, ARG_palette }; static const mp_arg_t allowed_args[] = { - { MP_QSTR_full_res, MP_ARG_BOOL, {.u_bool = false} } + { MP_QSTR_full_res, MP_ARG_BOOL, {.u_bool = false} }, + { MP_QSTR_palette, MP_ARG_BOOL, {.u_bool = false} }, }; // Parse args. @@ -186,10 +204,12 @@ mp_obj_t Presto_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, self->height = HEIGHT; } + self->using_palette = args[ARG_palette].u_bool; + presto_debug("m_new_class(ST7701...\n"); self->presto = m_new_class(ST7701, self->width, self->height, ROTATE_0, SPIPins{spi1, LCD_CS, LCD_CLK, LCD_DAT, PIN_UNUSED, LCD_DC, BACKLIGHT}, - presto_buffer, + presto_buffer, self->using_palette ? presto_palette : nullptr, LCD_D0); presto_debug("launch core1\n"); @@ -220,6 +240,10 @@ mp_int_t Presto_get_framebuffer(mp_obj_t self_in, mp_buffer_info_t *bufinfo, mp_ bufinfo->buf = presto_buffer + (self->width * self->height); // Return the remaining space, enough for three layers at 16bpp bufinfo->len = self->width * self->height * 2 * 3; + } else if (self->using_palette) { + // Full res palette mode, there is enough space for a single layer + bufinfo->buf = (uint8_t*)presto_buffer + (self->width * self->height); + bufinfo->len = self->width * self->height; } else { // Just return the buffer as-is, this is not really useful for much // other than doing fast writes *directly* to the front buffer diff --git a/modules/py_frozen/presto.py b/modules/py_frozen/presto.py index 0335303..246db8e 100644 --- a/modules/py_frozen/presto.py +++ b/modules/py_frozen/presto.py @@ -6,7 +6,7 @@ from collections import namedtuple from machine import Pin, PWM -from picographics import PicoGraphics, DISPLAY_PRESTO, DISPLAY_PRESTO_FULL_RES +from picographics import PicoGraphics, DISPLAY_PRESTO, DISPLAY_PRESTO_FULL_RES, PEN_RGB565, PEN_P8 Touch = namedtuple("touch", ("x", "y", "touched")) @@ -30,7 +30,7 @@ class Presto(): NUM_LEDS = 7 LED_PIN = 33 - def __init__(self, full_res=False, ambient_light=False, direct_to_fb=False, layers=None): + def __init__(self, full_res=False, palette=False, ambient_light=False, direct_to_fb=False, layers=None): # WiFi - *must* happen before Presto bringup # Note: Forces WiFi details to be in secrets.py self.wifi = EzWiFi() @@ -41,9 +41,10 @@ def __init__(self, full_res=False, ambient_light=False, direct_to_fb=False, laye # Display Driver & PicoGraphics if layers is None: layers = 1 if full_res else 2 - self.presto = _presto.Presto(full_res=full_res) - self.buffer = None if (full_res and not direct_to_fb) else memoryview(self.presto) - self.display = PicoGraphics(DISPLAY_PRESTO_FULL_RES if full_res else DISPLAY_PRESTO, buffer=self.buffer, layers=layers) + pen = PEN_P8 if palette else PEN_RGB565 + self.presto = _presto.Presto(full_res=full_res, palette=palette) + self.buffer = None if (full_res and not palette and not direct_to_fb) else memoryview(self.presto) + self.display = PicoGraphics(DISPLAY_PRESTO_FULL_RES if full_res else DISPLAY_PRESTO, buffer=self.buffer, layers=layers, pen_type=pen) self.width, self.height = self.display.get_bounds() if ambient_light: