From 066e6b9fc8dfb0fd19da2c87919d12da538ea63f Mon Sep 17 00:00:00 2001 From: liu weikai Date: Tue, 16 Jun 2026 23:49:11 +0800 Subject: [PATCH] fix(esp): prevent map SD reads from starving display --- .../src/ui/screens/gps/gps_page_runtime.cpp | 23 ++++++++++--------- ...test_runtime_map_presentation_adapters.cpp | 16 +++++++++++++ .../src/ui/widgets/map/map_tiles.cpp | 22 ++++++++++++++---- .../boards/src/display/DisplayInterface.cpp | 19 +++++++++++++++ .../platform/esp/common/shared_spi_lock.h | 4 ++++ .../esp/idf_common/src/display_spi_lock.cpp | 17 ++++++++++++++ 6 files changed, 86 insertions(+), 15 deletions(-) diff --git a/modules/ui_shared/src/ui/screens/gps/gps_page_runtime.cpp b/modules/ui_shared/src/ui/screens/gps/gps_page_runtime.cpp index 4d982353..cff4786c 100644 --- a/modules/ui_shared/src/ui/screens/gps/gps_page_runtime.cpp +++ b/modules/ui_shared/src/ui/screens/gps/gps_page_runtime.cpp @@ -229,6 +229,13 @@ uint8_t current_map_zoom() std::min(s_map_zoom, ::ui::widgets::map::kMaxZoom))); } +bool has_valid_viewport_center(const ::ui::map::MapViewport& viewport) +{ + return std::isfinite(viewport.center_lat) && + std::isfinite(viewport.center_lon) && + (viewport.center_lat != 0.0 || viewport.center_lon != 0.0); +} + void sync_workspace_layers_from_renderer() { auto& state = ::ui::presentation_sources::runtime_map_workspace_state(); @@ -244,13 +251,10 @@ void sync_workspace_viewport_from_renderer() auto& model = map_workspace_model(); auto viewport = model.viewport(); viewport.zoom = current_map_zoom(); - if (!std::isfinite(viewport.center_lat) || !std::isfinite(viewport.center_lon) || - (viewport.center_lat == 0.0 && viewport.center_lon == 0.0)) + if (has_valid_viewport_center(viewport)) { - viewport.center_lat = gps_ui::kDefaultLat; - viewport.center_lon = gps_ui::kDefaultLng; + (void)model.setViewport(viewport); } - (void)model.setViewport(viewport); (void)model.setActiveTool(::ui::map::MapToolKind::Pan); } @@ -300,19 +304,16 @@ bool commit_pending_map_pan_from_screen() const auto& config = app::configFacade().getConfig(); ::ui::widgets::map::Model model{}; - const bool has_viewport_center = - std::isfinite(snapshot.viewport.center_lat) && - std::isfinite(snapshot.viewport.center_lon) && - (snapshot.viewport.center_lat != 0.0 || snapshot.viewport.center_lon != 0.0); + const bool has_viewport_center = has_valid_viewport_center(snapshot.viewport); model.focus_point.valid = has_viewport_center || snapshot.self.valid; model.focus_point.lat = has_viewport_center ? snapshot.viewport.center_lat : (snapshot.self.valid ? snapshot.self.lat - : gps_ui::kDefaultLat); + : 0.0); model.focus_point.lon = has_viewport_center ? snapshot.viewport.center_lon : (snapshot.self.valid ? snapshot.self.lon - : gps_ui::kDefaultLng); + : 0.0); model.zoom = snapshot.viewport.zoom == 0 ? s_map_zoom : snapshot.viewport.zoom; model.pan_x = s_map_pan_x; model.pan_y = s_map_pan_y; diff --git a/modules/ui_shared/tests/test_runtime_map_presentation_adapters.cpp b/modules/ui_shared/tests/test_runtime_map_presentation_adapters.cpp index 5ef85944..bb30812e 100644 --- a/modules/ui_shared/tests/test_runtime_map_presentation_adapters.cpp +++ b/modules/ui_shared/tests/test_runtime_map_presentation_adapters.cpp @@ -160,6 +160,22 @@ int main() assert(snapshot.team.visible_members == 2); assert(snapshot.team.stale_members == 1); + state.has_viewport = false; + state.last_viewport = {}; + gps.snapshot.fix_valid = false; + request.requested_viewport = viewport(0.0, 0.0, 12); + assert(source.buildMapWorkspaceSnapshot(request, snapshot)); + assert(snapshot.header.valid); + assert(!snapshot.self.valid); + assert(!snapshot.can_center_on_self); + assert(snapshot.viewport.center_lat == 0.0); + assert(snapshot.viewport.center_lon == 0.0); + assert(snapshot.viewport.zoom == 12); + + gps.snapshot.fix_valid = true; + gps.snapshot.latitude = 52.4068; + gps.snapshot.longitude = -1.5197; + const auto center = sink.centerOnSelf(); assert(center.ok); assert(state.has_viewport); diff --git a/platform/esp/arduino_common/src/ui/widgets/map/map_tiles.cpp b/platform/esp/arduino_common/src/ui/widgets/map/map_tiles.cpp index 7a6404fb..fe8062b0 100644 --- a/platform/esp/arduino_common/src/ui/widgets/map/map_tiles.cpp +++ b/platform/esp/arduino_common/src/ui/widgets/map/map_tiles.cpp @@ -207,7 +207,8 @@ constexpr uint32_t kMapTileDiagnosticLogIntervalMs = 1000; constexpr TickType_t kMapTileWorkerPostCommandYieldTicks = pdMS_TO_TICKS(32); constexpr uint32_t kMapTileDisplaySpiSlowHoldMs = 8; constexpr uint32_t kMapTileDisplaySpiNormalCooldownMs = 160; -constexpr uint32_t kMapTileDisplaySpiSlowCooldownMs = 600; +constexpr uint32_t kMapTileDisplaySpiSlowCooldownMs = 1500; +constexpr uint32_t kMapTileDisplayPressureCooldownMs = 1500; uint32_t g_map_tile_decode_log_ms = 0; uint32_t g_map_tile_event_log_ms = 0; @@ -860,6 +861,19 @@ class EspMapTileBusArbiter final : public sys::runtime::IBusArbiter const sys::runtime::BusAcquireRequest& request) override { const uint32_t start_ms = sys::millis_now(); + if (::platform::esp::common::display_spi_recently_timed_out( + start_ms, + kMapTileDisplayPressureCooldownMs)) + { + sys::runtime::BusAcquireResult result{}; + result.status = sys::runtime::BusAcquireStatus::Busy; + result.diagnostics.resource = request.resource; + result.diagnostics.command_id = request.command_id; + result.diagnostics.policy = sys::runtime::BusAccessPolicy::DisplayFrameCritical; + updateHealth(result.status, start_ms); + return result; + } + if (static_cast(cooldown_until_ms_ - start_ms) > 0) { sys::runtime::BusAcquireResult result{}; @@ -1844,11 +1858,11 @@ void tileToLatLng(int tile_x, int tile_y, int zoom, double& lat, double& lng) */ void get_screen_center_lat_lng(const TileContext& ctx, double& lat, double& lng) { + lat = 0.0; + lng = 0.0; + if (!ctx.map_container || !ctx.anchor || !ctx.anchor->valid) { - // If anchor is invalid, return default London coordinates - lat = gps_ui::kDefaultLat; - lng = gps_ui::kDefaultLng; return; } diff --git a/platform/esp/boards/src/display/DisplayInterface.cpp b/platform/esp/boards/src/display/DisplayInterface.cpp index 07ddf279..1dcb12a7 100644 --- a/platform/esp/boards/src/display/DisplayInterface.cpp +++ b/platform/esp/boards/src/display/DisplayInterface.cpp @@ -19,6 +19,7 @@ constexpr uint32_t kDisplayLockTimeoutLogIntervalMs = 1000; uint32_t s_last_display_lock_timeout_log_ms = 0; uint32_t s_suppressed_display_lock_timeout_logs = 0; +volatile uint32_t s_last_display_spi_timeout_ms = 0; bool lock_or_log(LilyGoDispArduinoSPI& spi, TickType_t wait_ticks, const char* op) { @@ -28,6 +29,7 @@ bool lock_or_log(LilyGoDispArduinoSPI& spi, TickType_t wait_ticks, const char* o } const uint32_t now_ms = millis(); + ::platform::esp::common::note_display_spi_timeout(now_ms); ++s_suppressed_display_lock_timeout_logs; if (s_last_display_lock_timeout_log_ms == 0 || now_ms - s_last_display_lock_timeout_log_ms >= kDisplayLockTimeoutLogIntervalMs) @@ -59,6 +61,23 @@ void shared_spi_unlock() } } +void note_display_spi_timeout(uint32_t now_ms) +{ + s_last_display_spi_timeout_ms = now_ms; +} + +uint32_t last_display_spi_timeout_ms() +{ + return s_last_display_spi_timeout_ms; +} + +bool display_spi_recently_timed_out(uint32_t now_ms, uint32_t window_ms) +{ + const uint32_t last_timeout_ms = s_last_display_spi_timeout_ms; + return last_timeout_ms != 0 && + static_cast(now_ms - last_timeout_ms) < window_ms; +} + } // namespace platform::esp::common bool LilyGoDispArduinoSPI::lock(TickType_t xTicksToWait) diff --git a/platform/esp/common/include/platform/esp/common/shared_spi_lock.h b/platform/esp/common/include/platform/esp/common/shared_spi_lock.h index 196a26b3..3ef82258 100644 --- a/platform/esp/common/include/platform/esp/common/shared_spi_lock.h +++ b/platform/esp/common/include/platform/esp/common/shared_spi_lock.h @@ -1,6 +1,7 @@ #pragma once #include "freertos/FreeRTOS.h" +#include namespace platform::esp::common { @@ -10,6 +11,9 @@ namespace platform::esp::common // share one controller. It is not display-specific. bool shared_spi_lock(TickType_t xTicksToWait); void shared_spi_unlock(); +void note_display_spi_timeout(uint32_t now_ms); +uint32_t last_display_spi_timeout_ms(); +bool display_spi_recently_timed_out(uint32_t now_ms, uint32_t window_ms); class SharedSpiLockGuard { diff --git a/platform/esp/idf_common/src/display_spi_lock.cpp b/platform/esp/idf_common/src/display_spi_lock.cpp index eab604dd..65856c5e 100644 --- a/platform/esp/idf_common/src/display_spi_lock.cpp +++ b/platform/esp/idf_common/src/display_spi_lock.cpp @@ -11,4 +11,21 @@ bool shared_spi_lock(TickType_t xTicksToWait) void shared_spi_unlock() {} +void note_display_spi_timeout(uint32_t now_ms) +{ + (void)now_ms; +} + +uint32_t last_display_spi_timeout_ms() +{ + return 0; +} + +bool display_spi_recently_timed_out(uint32_t now_ms, uint32_t window_ms) +{ + (void)now_ms; + (void)window_ms; + return false; +} + } // namespace platform::esp::common