diff --git a/src/displayapp/DisplayApp.cpp b/src/displayapp/DisplayApp.cpp index 918dd9f9..98a28258 100644 --- a/src/displayapp/DisplayApp.cpp +++ b/src/displayapp/DisplayApp.cpp @@ -157,15 +157,20 @@ void DisplayApp::InitHw() { } TickType_t DisplayApp::CalculateSleepTime() { - TickType_t ticksElapsed = xTaskGetTickCount() - alwaysOnStartTime; - // Divide both the numerator and denominator by 8 to increase the number of ticks (frames) before the overflow tick is reached + // Calculates how many system ticks DisplayApp should sleep before rendering the next AOD frame + // Next frame time is frame count * refresh period (ms) * tick rate + auto RoundedDiv = [](uint32_t a, uint32_t b) { return ((a + (b / 2)) / b); }; - TickType_t elapsedTarget = RoundedDiv((configTICK_RATE_HZ / 8) * alwaysOnTickCount * alwaysOnRefreshPeriod, 1000 / 8); // RoundedDiv overflows when numerator + (denominator floordiv 2) > uint32 max - // in this case around 9 hours - constexpr TickType_t overflowTick = (UINT32_MAX - (1000 / 16)) / ((configTICK_RATE_HZ / 8) * alwaysOnRefreshPeriod); + // in this case around 9 hours (=overflow frame count / always on refresh period) + constexpr TickType_t overflowFrameCount = (UINT32_MAX - (1000 / 16)) / ((configTICK_RATE_HZ / 8) * alwaysOnRefreshPeriod); + + TickType_t ticksElapsed = xTaskGetTickCount() - alwaysOnStartTime; + // Divide both the numerator and denominator by 8 (=GCD(1000,1024)) + // to increase the number of ticks (frames) before the overflow tick is reached + TickType_t targetRenderTick = RoundedDiv((configTICK_RATE_HZ / 8) * alwaysOnFrameCount * alwaysOnRefreshPeriod, 1000 / 8); // Assumptions @@ -173,17 +178,17 @@ TickType_t DisplayApp::CalculateSleepTime() { // Needed for division trick above static_assert(configTICK_RATE_HZ % 8 == 0); - // Local tick count must always wraparound before the system tick count does - // As a static assert we can use 64 bit ints and therefore dodge overflows + // Frame count must always wraparound more often than the system tick count does // Always on overflow time (ms) < system tick overflow time (ms) - static_assert((uint64_t) overflowTick * (uint64_t) alwaysOnRefreshPeriod < (uint64_t) UINT32_MAX * 1000ULL / configTICK_RATE_HZ); + // Using 64bit ints here to avoid overflow + static_assert((uint64_t) overflowFrameCount * (uint64_t) alwaysOnRefreshPeriod < (uint64_t) UINT32_MAX * 1000ULL / configTICK_RATE_HZ); - if (alwaysOnTickCount == overflowTick) { - alwaysOnTickCount = 0; + if (alwaysOnFrameCount == overflowFrameCount) { + alwaysOnFrameCount = 0; alwaysOnStartTime = xTaskGetTickCount(); } - if (elapsedTarget > ticksElapsed) { - return elapsedTarget - ticksElapsed; + if (targetRenderTick > ticksElapsed) { + return targetRenderTick - ticksElapsed; } else { return 0; } @@ -240,7 +245,7 @@ void DisplayApp::Refresh() { if (lv_task_handler() > 0) { // Drop frames that we've missed if drawing/event handling took way longer than expected while (queueTimeout == 0) { - alwaysOnTickCount += 1; + alwaysOnFrameCount += 1; queueTimeout = CalculateSleepTime(); } } @@ -311,7 +316,7 @@ void DisplayApp::Refresh() { if (msg == Messages::GoToAOD) { lcd.LowPowerOn(); // Record idle entry time - alwaysOnTickCount = 0; + alwaysOnFrameCount = 0; alwaysOnStartTime = xTaskGetTickCount(); PushMessageToSystemTask(Pinetime::System::Messages::OnDisplayTaskAOD); state = States::AOD; diff --git a/src/displayapp/DisplayApp.h b/src/displayapp/DisplayApp.h index 73492808..2f276eaf 100644 --- a/src/displayapp/DisplayApp.h +++ b/src/displayapp/DisplayApp.h @@ -139,7 +139,7 @@ namespace Pinetime { bool isDimmed = false; TickType_t CalculateSleepTime(); - TickType_t alwaysOnTickCount; + TickType_t alwaysOnFrameCount; TickType_t alwaysOnStartTime; // If this is to be changed, make sure the actual always on refresh rate is changed // by configuring the LCD refresh timings