Skip to content

Commit c36e48c

Browse files
committed
aod: constant frequency idle frames
1 parent 31230c0 commit c36e48c

File tree

2 files changed

+55
-1
lines changed

2 files changed

+55
-1
lines changed

src/displayapp/DisplayApp.cpp

+48-1
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,36 @@ void DisplayApp::InitHw() {
153153
lcd.Init();
154154
}
155155

156+
TickType_t DisplayApp::CalculateSleepTime() {
157+
TickType_t ticksElapsed = xTaskGetTickCount() - alwaysOnStartTime;
158+
// Divide both the numerator and denominator by 8 to increase the number of ticks (frames) before the overflow tick is reached
159+
TickType_t elapsedTarget = ROUNDED_DIV((configTICK_RATE_HZ / 8) * alwaysOnTickCount * alwaysOnRefreshPeriod, 1000 / 8);
160+
// ROUNDED_DIV overflows when numerator + (denominator floordiv 2) > uint32 max
161+
// in this case around 9 hours
162+
constexpr TickType_t overflowTick = (UINT32_MAX - (1000 / 16)) / ((configTICK_RATE_HZ / 8) * alwaysOnRefreshPeriod);
163+
164+
// Assumptions
165+
166+
// Tick rate is multiple of 8
167+
// Needed for division trick above
168+
static_assert(configTICK_RATE_HZ % 8 == 0);
169+
170+
// Local tick count must always wraparound before the system tick count does
171+
// As a static assert we can use 64 bit ints and therefore dodge overflows
172+
// Always on overflow time (ms) < system tick overflow time (ms)
173+
static_assert((uint64_t) overflowTick * (uint64_t) alwaysOnRefreshPeriod < (uint64_t) UINT32_MAX * 1000ULL / configTICK_RATE_HZ);
174+
175+
if (alwaysOnTickCount == overflowTick) {
176+
alwaysOnTickCount = 0;
177+
alwaysOnStartTime = xTaskGetTickCount();
178+
}
179+
if (elapsedTarget > ticksElapsed) {
180+
return elapsedTarget - ticksElapsed;
181+
} else {
182+
return 0;
183+
}
184+
}
185+
156186
void DisplayApp::Refresh() {
157187
auto LoadPreviousScreen = [this]() {
158188
FullRefreshDirections returnDirection;
@@ -203,7 +233,21 @@ void DisplayApp::Refresh() {
203233
switch (state) {
204234
case States::Idle:
205235
if (settingsController.GetAlwaysOnDisplay()) {
206-
queueTimeout = lv_task_handler();
236+
if (!currentScreen->IsRunning()) {
237+
LoadPreviousScreen();
238+
}
239+
// Check we've slept long enough
240+
// Might not be true if the loop received an event
241+
// If not true, then wait that amount of time
242+
queueTimeout = CalculateSleepTime();
243+
if (queueTimeout == 0) {
244+
lv_task_handler();
245+
// Drop frames that we've missed if the loop took way longer than expected to execute
246+
while (queueTimeout == 0) {
247+
alwaysOnTickCount += 1;
248+
queueTimeout = CalculateSleepTime();
249+
}
250+
}
207251
} else {
208252
queueTimeout = portMAX_DELAY;
209253
}
@@ -246,6 +290,9 @@ void DisplayApp::Refresh() {
246290
if (settingsController.GetAlwaysOnDisplay()) {
247291
brightnessController.Set(Controllers::BrightnessController::Levels::AlwaysOn);
248292
lcd.LowPowerOn();
293+
// Record idle entry time
294+
alwaysOnTickCount = 0;
295+
alwaysOnStartTime = xTaskGetTickCount();
249296
} else {
250297
brightnessController.Set(Controllers::BrightnessController::Levels::Off);
251298
lcd.Sleep();

src/displayapp/DisplayApp.h

+7
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,13 @@ namespace Pinetime {
135135
Utility::StaticStack<FullRefreshDirections, returnAppStackSize> appStackDirections;
136136

137137
bool isDimmed = false;
138+
139+
TickType_t CalculateSleepTime();
140+
TickType_t alwaysOnTickCount;
141+
TickType_t alwaysOnStartTime;
142+
// If this is to be changed, make sure the actual always on refresh rate is changed
143+
// by configuring the LCD refresh timings
144+
static constexpr uint32_t alwaysOnRefreshPeriod = 500;
138145
};
139146
}
140147
}

0 commit comments

Comments
 (0)