aod: constant frequency idle frames

This commit is contained in:
mark9064 2024-01-25 22:28:53 +00:00 committed by JF
parent ef88e8165c
commit 2bb611db8e
2 changed files with 55 additions and 1 deletions

View file

@ -154,6 +154,36 @@ void DisplayApp::InitHw() {
lcd.Init(); lcd.Init();
} }
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
TickType_t elapsedTarget = ROUNDED_DIV((configTICK_RATE_HZ / 8) * alwaysOnTickCount * alwaysOnRefreshPeriod, 1000 / 8);
// ROUNDED_DIV 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);
// Assumptions
// Tick rate is multiple of 8
// 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
// 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);
if (alwaysOnTickCount == overflowTick) {
alwaysOnTickCount = 0;
alwaysOnStartTime = xTaskGetTickCount();
}
if (elapsedTarget > ticksElapsed) {
return elapsedTarget - ticksElapsed;
} else {
return 0;
}
}
void DisplayApp::Refresh() { void DisplayApp::Refresh() {
auto LoadPreviousScreen = [this]() { auto LoadPreviousScreen = [this]() {
FullRefreshDirections returnDirection; FullRefreshDirections returnDirection;
@ -204,7 +234,21 @@ void DisplayApp::Refresh() {
switch (state) { switch (state) {
case States::Idle: case States::Idle:
if (settingsController.GetAlwaysOnDisplay()) { if (settingsController.GetAlwaysOnDisplay()) {
queueTimeout = lv_task_handler(); if (!currentScreen->IsRunning()) {
LoadPreviousScreen();
}
// Check we've slept long enough
// Might not be true if the loop received an event
// If not true, then wait that amount of time
queueTimeout = CalculateSleepTime();
if (queueTimeout == 0) {
lv_task_handler();
// Drop frames that we've missed if the loop took way longer than expected to execute
while (queueTimeout == 0) {
alwaysOnTickCount += 1;
queueTimeout = CalculateSleepTime();
}
}
} else { } else {
queueTimeout = portMAX_DELAY; queueTimeout = portMAX_DELAY;
} }
@ -247,6 +291,9 @@ void DisplayApp::Refresh() {
if (settingsController.GetAlwaysOnDisplay()) { if (settingsController.GetAlwaysOnDisplay()) {
brightnessController.Set(Controllers::BrightnessController::Levels::AlwaysOn); brightnessController.Set(Controllers::BrightnessController::Levels::AlwaysOn);
lcd.LowPowerOn(); lcd.LowPowerOn();
// Record idle entry time
alwaysOnTickCount = 0;
alwaysOnStartTime = xTaskGetTickCount();
} else { } else {
brightnessController.Set(Controllers::BrightnessController::Levels::Off); brightnessController.Set(Controllers::BrightnessController::Levels::Off);
lcd.Sleep(); lcd.Sleep();

View file

@ -135,6 +135,13 @@ namespace Pinetime {
Utility::StaticStack<FullRefreshDirections, returnAppStackSize> appStackDirections; Utility::StaticStack<FullRefreshDirections, returnAppStackSize> appStackDirections;
bool isDimmed = false; bool isDimmed = false;
TickType_t CalculateSleepTime();
TickType_t alwaysOnTickCount;
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
static constexpr uint32_t alwaysOnRefreshPeriod = 500;
}; };
} }
} }