'Fix' issues with screen tearing

This commit is contained in:
shylie 2026-02-26 16:56:03 -05:00
parent 19491d6038
commit 58c1b9626b
9 changed files with 138 additions and 136 deletions

View File

@ -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

View File

@ -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);

View File

@ -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

View File

@ -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]);
}
}
}

View File

@ -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<menus::ManaMenu>(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;

View File

@ -1,6 +1,5 @@
#include "icons.h"
#include "menu.h"
#include "pixelstream.h"
#include <cmath>
@ -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()

View File

@ -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]);
}
}

View File

@ -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);

View File

@ -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);