'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_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
set(CMAKE_RUNTIME_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() pico_sdk_init()
add_library(devicelib STATIC add_library(devicelib STATIC

View File

@ -19,6 +19,8 @@ public:
uint16_t height); uint16_t height);
void fill(uint16_t left, uint16_t top, uint16_t width, 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); 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(); PixelStream pixels();
void clear(); void clear();
@ -27,6 +29,10 @@ private:
uint8_t sck, tx, cs, dc; uint8_t sck, tx, cs, dc;
spi_inst_t* spi; spi_inst_t* spi;
int dma; 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);
void send_command(uint8_t command, uint8_t param); void send_command(uint8_t command, uint8_t param);

View File

@ -119,6 +119,7 @@ private:
uint8_t previous; uint8_t previous;
bool going_right; bool going_right;
float progress; float progress;
float old_progress;
}; };
class CMCMenu : public Menu class CMCMenu : public Menu
@ -144,6 +145,7 @@ private:
uint8_t current; uint8_t current;
uint8_t previous; uint8_t previous;
float progress; float progress;
float old_progress;
}; };
class SelectMenu : public Menu class SelectMenu : public Menu

View File

@ -1,6 +1,5 @@
#include "icons.h" #include "icons.h"
#include "menu.h" #include "menu.h"
#include "pixelstream.h"
using namespace menus; using namespace menus;
@ -20,10 +19,17 @@ void CMCMenu::onTick(float dt)
constexpr float CENTER_Y = 160; constexpr float CENTER_Y = 160;
constexpr float DT_SCALE = 1.4f; constexpr float DT_SCALE = 1.4f;
constexpr uint8_t STEP = 48; constexpr uint8_t STEP = 48;
constexpr uint8_t ERASE_DATA[39 * 5 * 3] = {};
old_progress = progress;
if (progress < 1.0f) if (progress < 1.0f)
{ {
progress += dt * DT_SCALE;
if (progress >= 0.75f)
{
progress = 1.0f;
}
for (int8_t dcmc = -1; dcmc <= 1; dcmc++) for (int8_t dcmc = -1; dcmc <= 1; dcmc++)
{ {
const int8_t dir = (previous > current ? 1 : -1); const int8_t dir = (previous > current ? 1 : -1);
@ -36,35 +42,16 @@ void CMCMenu::onTick(float dt)
const float offset const float offset
= dir * ease::outElastic(progress) * STEP + (dir * (dcmc - 1)) * STEP; = dir * ease::outElastic(progress) * STEP + (dir * (dcmc - 1)) * STEP;
const float old_offset = dir * ease::outElastic(old_progress) * STEP
+ (dir * (dcmc - 1)) * STEP;
// left // erase old
{ display->fill(CENTER_X - 15 + old_offset, CENTER_Y - 15, 32, 32, 0, 0,
display->set_update_area(CENTER_X - 20 + offset, CENTER_Y - 20, 5, 39); 0);
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);
}
// draw new // draw new
{ display->fill(CENTER_X - 15 + offset, CENTER_Y - 15, 32, 32,
display->set_update_area(CENTER_X - 15 + offset, CENTER_Y - 15, 31, icon::NUMBER_ICONS[cmc_to_draw]);
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;
} }
} }
} }

View File

@ -15,7 +15,7 @@ namespace
Display display(DISPLAY_SCK, DISPLAY_TX, DISPLAY_CS, DISPLAY_DC, Display display(DISPLAY_SCK, DISPLAY_TX, DISPLAY_CS, DISPLAY_DC,
100 * 1000 * 1000); 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 enum class RX
{ {
@ -54,6 +54,8 @@ volatile uint8_t debug_requested = false;
constexpr int MS_BUTTON_TIMEOUT = 500; constexpr int MS_BUTTON_TIMEOUT = 500;
constexpr int FREQ = 90 * 1000 * 1000;
} }
int main() int main()
@ -87,12 +89,15 @@ int main()
absolute_time_t last_press = get_absolute_time(); absolute_time_t last_press = get_absolute_time();
absolute_time_t previous_time = last_press; absolute_time_t previous_time = last_press;
float dt_accum = 0;
menus::push_menu<menus::ManaMenu>(display, flash); menus::push_menu<menus::ManaMenu>(display, flash);
while (true) while (true)
{ {
absolute_time_t current = get_absolute_time(); absolute_time_t current = get_absolute_time();
float dt = absolute_time_diff_us(previous_time, current) / 1000000.0f; float dt = absolute_time_diff_us(previous_time, current) / 1000000.0f;
dt_accum += dt;
if (!gpio_get(BUTTON_LEFT) if (!gpio_get(BUTTON_LEFT)
&& absolute_time_diff_us(last_press, current) && absolute_time_diff_us(last_press, current)
@ -122,7 +127,11 @@ int main()
} }
tud_task(); 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; previous_time = current;

View File

@ -1,6 +1,5 @@
#include "icons.h" #include "icons.h"
#include "menu.h" #include "menu.h"
#include "pixelstream.h"
#include <cmath> #include <cmath>
@ -18,7 +17,8 @@ ManaMenu::ManaMenu(lib::Display& display, lib::Flash& flash) :
current(0), current(0),
previous(0), previous(0),
going_right(true), 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_X = 120;
constexpr float CENTER_Y = 190; constexpr float CENTER_Y = 190;
constexpr float MAX_OFFSET = RADIUS / 1.5f; 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 float DT_SCALE_UPDOWN = 1.6f;
constexpr uint8_t ERASE_DATA[38 * 5 * 3] = {};
old_progress = progress;
if (progress < 1.0f) 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; progress += dt * DT_SCALE_UPDOWN;
if (progress > 0.75f) if (progress > 0.75f)
{ {
progress = 1.0f; 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) 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 const float offset
= (going_right ? -1 : 1) * STEP * ease::outElastic(progress - 1) = (going_right ? -1 : 1) * STEP * ease::outElastic(progress - 1)
+ STEP / 2.0f + (going_right ? 2 * STEP : 0); + STEP / 2.0f + (going_right ? 2 * STEP : 0);
@ -75,47 +72,15 @@ void ManaMenu::onTick(float dt)
// erase previous // erase previous
for (int i = 0; i < sizeof(COLORS) / sizeof(*COLORS); i++) 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 x = cosf(angle) * RADIUS;
const float y = sinf(angle) * RADIUS; const float y = sinf(angle) * RADIUS;
const int left = x - 20 + CENTER_X; const int left = x - 15 + CENTER_X;
const int top = y - 20 + CENTER_Y; const int top = y - 15 + CENTER_Y;
// top rectangle display->fill(left, top, 32, 32, 0, 0, 0);
{
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;
} }
// write new // write new
@ -129,43 +94,30 @@ void ManaMenu::onTick(float dt)
const int left = x - 15 + CENTER_X; const int left = x - 15 + CENTER_X;
const int top = y - 15 + CENTER_Y; 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]); 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) 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; progress += dt * DT_SCALE_UPDOWN;
if (progress > 2.75f) if (progress > 2.75f)
{ {
progress = 3.0f; 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() void ManaMenu::onResume()

View File

@ -1,7 +1,6 @@
#include "cardslot.h" #include "cardslot.h"
#include "icons.h" #include "icons.h"
#include "menu.h" #include "menu.h"
#include "pixelstream.h"
namespace namespace
{ {
@ -103,10 +102,13 @@ void SelectMenu::draw_letter(uint8_t x, uint8_t y, uint8_t letter)
return; return;
} }
const uint8_t* const letter_data if (letter >= 26)
= letter >= 26 ? ERASE_DATA : icon::LETTERS[letter]; {
display->set_update_area(x * 12, y * 19, 11, 20); display->fill(x * 12, y * 19, 12, 19, 0, 0, 0);
auto pixels = display->pixels(); }
pixels.write(letter_data, icon::TEXT_ICON_LENGTH); 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; time_since_last_display += dt;
if (time_since_last_display > 0.016f) if (time_since_last_display > 0.015f)
{ {
// ball // ball
draw_rectangle(old_ball_x, old_ball_y, 12, 12, 0, 0, 0); draw_rectangle(old_ball_x, old_ball_y, 12, 12, 0, 0, 0);

View File

@ -13,7 +13,7 @@ using namespace lib;
namespace namespace
{ {
constexpr uint PIXELS_PER_TRANSFER = 16; constexpr uint PIXELS_PER_TRANSFER = 8;
int dma = -1; int dma = -1;
uint8_t cs; uint8_t cs;
@ -28,6 +28,7 @@ void dma_handler()
dma_channel_set_read_addr(dma, color_buffer, transfers_left > 0); dma_channel_set_read_addr(dma, color_buffer, transfers_left > 0);
gpio_put(cs, 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), sck(sck),
tx(tx), tx(tx),
cs(cs), cs(cs),
dc(dc) dc(dc),
frame(false)
{ {
spi_inst_t* sck_spi = detail::get_spi_instance(sck); spi_inst_t* sck_spi = detail::get_spi_instance(sck);
spi_inst_t* tx_spi = detail::get_spi_instance(tx); 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); gpio_set_dir(dc, GPIO_OUT);
spi_init(spi, baudrate); spi_init(spi, baudrate);
spi_set_format(spi, 8, SPI_CPOL_1, SPI_CPHA_1, SPI_MSB_FIRST);
// initialize display command sequence // 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); sleep_ms(10);
clear(); 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, 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) if (dma >= 0)
{ {
while (transfers_left > 0) while (transfers_left > 0)
; {
dma_channel_wait_for_finish_blocking(dma);
}
for (int i = 0; i < PIXELS_PER_TRANSFER; i++) for (int i = 0; i < PIXELS_PER_TRANSFER; i++)
{ {
color_buffer[3 * i + 0] = r; 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); set_update_area(left, top, width, height);
begin_dma(color_buffer, 3 * PIXELS_PER_TRANSFER, 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);
} }
// fallback if DMA is not available // fallback if DMA is not available
else 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); } PixelStream Display::pixels() { return PixelStream(*this); }
void Display::clear() { fill(0, 0, 240, 320, 0, 0, 0); } 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) void Display::send_command(uint8_t command)
{ {
send_command(command, nullptr, 0); send_command(command, nullptr, 0);