From 58c1b9626b55b7e2f7aacc3487a3458959c6b8a7 Mon Sep 17 00:00:00 2001 From: shylie Date: Thu, 26 Feb 2026 16:56:03 -0500 Subject: [PATCH] 'Fix' issues with screen tearing --- CMakeLists.txt | 2 + include/display.h | 6 ++ programs/mtgcard/include/menu.h | 2 + programs/mtgcard/src/cmcmenu.cpp | 43 ++++----- programs/mtgcard/src/main.cpp | 13 ++- programs/mtgcard/src/manamenu.cpp | 134 +++++++++------------------- programs/mtgcard/src/selectmenu.cpp | 14 +-- programs/pong/src/main.cpp | 2 +- src/display.cpp | 58 ++++++++++-- 9 files changed, 138 insertions(+), 136 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 5cf0b13..fe7d229 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -8,6 +8,8 @@ set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin) set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin) set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin) +set(PICO_USE_FASTEST_SUPPORTED_CLOCK 1) + pico_sdk_init() add_library(devicelib STATIC diff --git a/include/display.h b/include/display.h index f05946d..8722891 100644 --- a/include/display.h +++ b/include/display.h @@ -19,6 +19,8 @@ public: uint16_t height); void fill(uint16_t left, uint16_t top, uint16_t width, uint16_t height, uint8_t r, uint8_t g, uint8_t b); + void fill(uint16_t left, uint16_t top, uint16_t width, uint16_t height, + const uint8_t* src); PixelStream pixels(); void clear(); @@ -27,6 +29,10 @@ private: uint8_t sck, tx, cs, dc; spi_inst_t* spi; int dma; + bool frame; + + void begin_dma(const volatile uint8_t* src, uint32_t transfer_count, + uint32_t pixel_count); void send_command(uint8_t command); void send_command(uint8_t command, uint8_t param); diff --git a/programs/mtgcard/include/menu.h b/programs/mtgcard/include/menu.h index 0088e60..8bda2b2 100644 --- a/programs/mtgcard/include/menu.h +++ b/programs/mtgcard/include/menu.h @@ -119,6 +119,7 @@ private: uint8_t previous; bool going_right; float progress; + float old_progress; }; class CMCMenu : public Menu @@ -144,6 +145,7 @@ private: uint8_t current; uint8_t previous; float progress; + float old_progress; }; class SelectMenu : public Menu diff --git a/programs/mtgcard/src/cmcmenu.cpp b/programs/mtgcard/src/cmcmenu.cpp index 8e1cb13..8c5de28 100644 --- a/programs/mtgcard/src/cmcmenu.cpp +++ b/programs/mtgcard/src/cmcmenu.cpp @@ -1,6 +1,5 @@ #include "icons.h" #include "menu.h" -#include "pixelstream.h" using namespace menus; @@ -20,10 +19,17 @@ void CMCMenu::onTick(float dt) constexpr float CENTER_Y = 160; constexpr float DT_SCALE = 1.4f; constexpr uint8_t STEP = 48; - constexpr uint8_t ERASE_DATA[39 * 5 * 3] = {}; + + old_progress = progress; if (progress < 1.0f) { + progress += dt * DT_SCALE; + if (progress >= 0.75f) + { + progress = 1.0f; + } + for (int8_t dcmc = -1; dcmc <= 1; dcmc++) { const int8_t dir = (previous > current ? 1 : -1); @@ -36,35 +42,16 @@ void CMCMenu::onTick(float dt) const float offset = dir * ease::outElastic(progress) * STEP + (dir * (dcmc - 1)) * STEP; + const float old_offset = dir * ease::outElastic(old_progress) * STEP + + (dir * (dcmc - 1)) * STEP; - // left - { - display->set_update_area(CENTER_X - 20 + offset, CENTER_Y - 20, 5, 39); - auto pixels = display->pixels(); - pixels.write(ERASE_DATA, 39 * 5 * 3); - } - // right - { - display->set_update_area(CENTER_X - 20 + offset + 37, CENTER_Y - 20, 5, - 39); - auto pixels = display->pixels(); - pixels.write(ERASE_DATA, 39 * 5 * 3); - } + // erase old + display->fill(CENTER_X - 15 + old_offset, CENTER_Y - 15, 32, 32, 0, 0, + 0); // draw new - { - display->set_update_area(CENTER_X - 15 + offset, CENTER_Y - 15, 31, - 32); - auto pixels = display->pixels(); - pixels.write(icon::NUMBER_ICONS[cmc_to_draw], icon::MTG_ICON_LENGTH); - } - } - - progress += dt * DT_SCALE; - - if (progress >= 0.75f) - { - progress = 1.0f; + display->fill(CENTER_X - 15 + offset, CENTER_Y - 15, 32, 32, + icon::NUMBER_ICONS[cmc_to_draw]); } } } diff --git a/programs/mtgcard/src/main.cpp b/programs/mtgcard/src/main.cpp index 3b95b91..7478e4c 100644 --- a/programs/mtgcard/src/main.cpp +++ b/programs/mtgcard/src/main.cpp @@ -15,7 +15,7 @@ namespace Display display(DISPLAY_SCK, DISPLAY_TX, DISPLAY_CS, DISPLAY_DC, 100 * 1000 * 1000); -Flash flash(FLASH_SCK, FLASH_TX, FLASH_RX, FLASH_CS, 25 * 1000 * 1000); +Flash flash(FLASH_SCK, FLASH_TX, FLASH_RX, FLASH_CS, 50 * 1000 * 1000); enum class RX { @@ -54,6 +54,8 @@ volatile uint8_t debug_requested = false; constexpr int MS_BUTTON_TIMEOUT = 500; +constexpr int FREQ = 90 * 1000 * 1000; + } int main() @@ -87,12 +89,15 @@ int main() absolute_time_t last_press = get_absolute_time(); absolute_time_t previous_time = last_press; + float dt_accum = 0; + menus::push_menu(display, flash); while (true) { absolute_time_t current = get_absolute_time(); float dt = absolute_time_diff_us(previous_time, current) / 1000000.0f; + dt_accum += dt; if (!gpio_get(BUTTON_LEFT) && absolute_time_diff_us(last_press, current) @@ -122,7 +127,11 @@ int main() } tud_task(); - menus::get_current_menu()->onTick(dt); + if (dt_accum > 1.0f / 60.0f) + { + menus::get_current_menu()->onTick(dt_accum); + dt_accum = 0; + } previous_time = current; diff --git a/programs/mtgcard/src/manamenu.cpp b/programs/mtgcard/src/manamenu.cpp index a28b23a..e7aa239 100644 --- a/programs/mtgcard/src/manamenu.cpp +++ b/programs/mtgcard/src/manamenu.cpp @@ -1,6 +1,5 @@ #include "icons.h" #include "menu.h" -#include "pixelstream.h" #include @@ -18,7 +17,8 @@ ManaMenu::ManaMenu(lib::Display& display, lib::Flash& flash) : current(0), previous(0), going_right(true), - progress(0.7f) + progress(0.7f), + old_progress(progress) { } @@ -30,44 +30,41 @@ void ManaMenu::onTick(float dt) constexpr float CENTER_X = 120; constexpr float CENTER_Y = 190; constexpr float MAX_OFFSET = RADIUS / 1.5f; - constexpr float DT_SCALE_ROTATE = 1.0f; + constexpr float DT_SCALE_ROTATE = 1.1f; constexpr float DT_SCALE_UPDOWN = 1.6f; - constexpr uint8_t ERASE_DATA[38 * 5 * 3] = {}; + + old_progress = progress; if (progress < 1.0f) { - const float offset - = (1.0f - ease::outElastic(progress)) * MAX_OFFSET + RADIUS; - - // erase old - { - display->set_update_area(CENTER_X - 20, CENTER_Y - 20 - offset, 38, 5); - auto pixels = display->pixels(); - pixels.write(ERASE_DATA, 38 * 5 * 3); - } - { - display->set_update_area(CENTER_X - 20, CENTER_Y - 15 + 30 - offset, 38, - 5); - auto pixels = display->pixels(); - pixels.write(ERASE_DATA, 38 * 5 * 3); - } - - // write new - { - display->set_update_area(CENTER_X - 15, CENTER_Y - 15 - offset, 31, 32); - auto pixels = display->pixels(); - const uint8_t* data = icon::wubrgc(COLORS[previous]); - pixels.write(data, icon::MTG_ICON_LENGTH); - } - progress += dt * DT_SCALE_UPDOWN; if (progress > 0.75f) { progress = 1.0f; } + + // erase old + const float old_offset + = (1.0f - ease::outElastic(old_progress)) * MAX_OFFSET + RADIUS; + display->fill(CENTER_X - 15, CENTER_Y - 15 - old_offset, 32, 32, 0, 0, 0); + + // write new + const float offset + = (1.0f - ease::outElastic(progress)) * MAX_OFFSET + RADIUS; + const uint8_t* data = icon::wubrgc(COLORS[previous]); + display->fill(CENTER_X - 15, CENTER_Y - 15 - offset, 32, 32, data); } else if (progress < 2.0f) { + progress += dt * DT_SCALE_ROTATE; + if (progress > 1.75f) + { + progress = 2.0f; + } + + const float old_offset + = (going_right ? -1 : 1) * STEP * ease::outElastic(old_progress - 1) + + STEP / 2.0f + (going_right ? 2 * STEP : 0); const float offset = (going_right ? -1 : 1) * STEP * ease::outElastic(progress - 1) + STEP / 2.0f + (going_right ? 2 * STEP : 0); @@ -75,47 +72,15 @@ void ManaMenu::onTick(float dt) // erase previous for (int i = 0; i < sizeof(COLORS) / sizeof(*COLORS); i++) { - const float angle = i * STEP - offset; + const float angle = i * STEP - old_offset; const float x = cosf(angle) * RADIUS; const float y = sinf(angle) * RADIUS; - const int left = x - 20 + CENTER_X; - const int top = y - 20 + CENTER_Y; + const int left = x - 15 + CENTER_X; + const int top = y - 15 + CENTER_Y; - // top rectangle - { - display->set_update_area(left, top, 38, 5); - auto pixels = display->pixels(); - pixels.write(ERASE_DATA, 38 * 5 * 3); - } - - // bottom rectangle - { - display->set_update_area(left, top + 36, 38, 5); - auto pixels = display->pixels(); - pixels.write(ERASE_DATA, 38 * 5 * 3); - } - - // left rectangle - { - display->set_update_area(left, top + 3, 5, 38); - auto pixels = display->pixels(); - pixels.write(ERASE_DATA, 38 * 5 * 3); - } - - // right rectangle - { - display->set_update_area(left + 36, top + 3, 5, 38); - auto pixels = display->pixels(); - pixels.write(ERASE_DATA, 38 * 5 * 3); - } - } - - progress += dt * DT_SCALE_ROTATE; - if (progress > 1.75f) - { - progress = 2.0f; + display->fill(left, top, 32, 32, 0, 0, 0); } // write new @@ -129,43 +94,30 @@ void ManaMenu::onTick(float dt) const int left = x - 15 + CENTER_X; const int top = y - 15 + CENTER_Y; - display->set_update_area(left, top, 31, 32); - auto pixels = display->pixels(); const uint8_t* data = icon::wubrgc(COLORS[i]); - pixels.write(data, icon::MTG_ICON_LENGTH); + display->fill(left, top, 32, 32, data); } } else if (progress < 3.0f) { - const float offset - = ease::outElastic(progress - 2.0f) * MAX_OFFSET + RADIUS; - - // erase old - { - display->set_update_area(CENTER_X - 20, CENTER_Y - 20 - offset, 38, 5); - auto pixels = display->pixels(); - pixels.write(ERASE_DATA, 38 * 5 * 3); - } - { - display->set_update_area(CENTER_X - 20, CENTER_Y - 15 + 30 - offset, 38, - 5); - auto pixels = display->pixels(); - pixels.write(ERASE_DATA, 38 * 5 * 3); - } - - // write new - { - display->set_update_area(CENTER_X - 15, CENTER_Y - 15 - offset, 31, 32); - auto pixels = display->pixels(); - const uint8_t* data = icon::wubrgc(COLORS[current]); - pixels.write(data, icon::MTG_ICON_LENGTH); - } - progress += dt * DT_SCALE_UPDOWN; if (progress > 2.75f) { progress = 3.0f; } + + const float old_offset + = ease::outElastic(old_progress - 2.0f) * MAX_OFFSET + RADIUS; + const float offset + = ease::outElastic(progress - 2.0f) * MAX_OFFSET + RADIUS; + + display->fill(CENTER_X - 15, CENTER_Y - 15 - old_offset, 32, 32, 0, 0, 0); + + // write new + { + const uint8_t* data = icon::wubrgc(COLORS[current]); + display->fill(CENTER_X - 15, CENTER_Y - 15 - offset, 32, 32, data); + } } } void ManaMenu::onResume() diff --git a/programs/mtgcard/src/selectmenu.cpp b/programs/mtgcard/src/selectmenu.cpp index f859d5a..30a2df5 100644 --- a/programs/mtgcard/src/selectmenu.cpp +++ b/programs/mtgcard/src/selectmenu.cpp @@ -1,7 +1,6 @@ #include "cardslot.h" #include "icons.h" #include "menu.h" -#include "pixelstream.h" namespace { @@ -103,10 +102,13 @@ void SelectMenu::draw_letter(uint8_t x, uint8_t y, uint8_t letter) return; } - const uint8_t* const letter_data - = letter >= 26 ? ERASE_DATA : icon::LETTERS[letter]; + if (letter >= 26) + { - display->set_update_area(x * 12, y * 19, 11, 20); - auto pixels = display->pixels(); - pixels.write(letter_data, icon::TEXT_ICON_LENGTH); + display->fill(x * 12, y * 19, 12, 19, 0, 0, 0); + } + else + { + display->fill(x * 12, y * 19, 12, 19, icon::LETTERS[letter]); + } } diff --git a/programs/pong/src/main.cpp b/programs/pong/src/main.cpp index 6b85532..f651e96 100644 --- a/programs/pong/src/main.cpp +++ b/programs/pong/src/main.cpp @@ -117,7 +117,7 @@ int main() time_since_last_display += dt; - if (time_since_last_display > 0.016f) + if (time_since_last_display > 0.015f) { // ball draw_rectangle(old_ball_x, old_ball_y, 12, 12, 0, 0, 0); diff --git a/src/display.cpp b/src/display.cpp index 4082df2..51cbcfd 100644 --- a/src/display.cpp +++ b/src/display.cpp @@ -13,7 +13,7 @@ using namespace lib; namespace { -constexpr uint PIXELS_PER_TRANSFER = 16; +constexpr uint PIXELS_PER_TRANSFER = 8; int dma = -1; uint8_t cs; @@ -28,6 +28,7 @@ void dma_handler() dma_channel_set_read_addr(dma, color_buffer, transfers_left > 0); gpio_put(cs, transfers_left <= 0); + busy_wait_at_least_cycles(35); } } @@ -37,7 +38,8 @@ Display::Display(uint8_t sck, uint8_t tx, uint8_t cs, uint8_t dc, sck(sck), tx(tx), cs(cs), - dc(dc) + dc(dc), + frame(false) { spi_inst_t* sck_spi = detail::get_spi_instance(sck); spi_inst_t* tx_spi = detail::get_spi_instance(tx); @@ -85,6 +87,7 @@ Display::Display(uint8_t sck, uint8_t tx, uint8_t cs, uint8_t dc, gpio_set_dir(dc, GPIO_OUT); spi_init(spi, baudrate); + spi_set_format(spi, 8, SPI_CPOL_1, SPI_CPHA_1, SPI_MSB_FIRST); // initialize display command sequence // ----------------------------------- @@ -118,6 +121,14 @@ Display::Display(uint8_t sck, uint8_t tx, uint8_t cs, uint8_t dc, sleep_ms(10); clear(); + sleep_ms(25); + + // set framerate + send_command(0xC6, 0x00); + { + constexpr uint8_t params[5] = { 0x01, 0x01, 0x01, 0x11, 0x11 }; + send_command(0xB2, params, 5); + } } void Display::set_update_area(uint16_t left, uint16_t top, uint16_t width, @@ -149,7 +160,10 @@ void Display::fill(uint16_t left, uint16_t top, uint16_t width, if (dma >= 0) { while (transfers_left > 0) - ; + { + dma_channel_wait_for_finish_blocking(dma); + } + for (int i = 0; i < PIXELS_PER_TRANSFER; i++) { color_buffer[3 * i + 0] = r; @@ -158,11 +172,7 @@ void Display::fill(uint16_t left, uint16_t top, uint16_t width, } set_update_area(left, top, width, height); - - gpio_put(cs, false); - send_command_and_begin_data_stream(0x2C); - transfers_left = width * height + PIXELS_PER_TRANSFER; - dma_channel_set_read_addr(dma, color_buffer, true); + begin_dma(color_buffer, 3 * PIXELS_PER_TRANSFER, width * height); } // fallback if DMA is not available else @@ -177,10 +187,42 @@ void Display::fill(uint16_t left, uint16_t top, uint16_t width, } } +void Display::fill(uint16_t left, uint16_t top, uint16_t width, + uint16_t height, const uint8_t* src) +{ + if (dma >= 0) + { + while (transfers_left > 0) + { + dma_channel_wait_for_finish_blocking(dma); + } + + set_update_area(left, top, width, height); + // pixel count is set to 1 since this is the whole buffer + begin_dma(src, width * height * 3, 1); + } + // fallback if DMA is not available + else + { + set_update_area(left, top, width, height); + + pixels().write(src, width * height * 3); + } +} + PixelStream Display::pixels() { return PixelStream(*this); } void Display::clear() { fill(0, 0, 240, 320, 0, 0, 0); } +void Display::begin_dma(const volatile uint8_t* src, uint32_t transfer_count, + uint32_t pixel_count) +{ + gpio_put(cs, false); + send_command_and_begin_data_stream(0x2C); + transfers_left = pixel_count; + dma_channel_transfer_from_buffer_now(dma, src, transfer_count); +} + void Display::send_command(uint8_t command) { send_command(command, nullptr, 0);