Improve sleep time calculation docs

This commit is contained in:
mark9064 2024-09-22 00:34:34 +01:00
parent 3ebc25dd4b
commit 7ced109181
2 changed files with 20 additions and 15 deletions

View file

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

View file

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