Skip to content

Commit 2bb611d

Browse files
mark9064JF002
authored andcommitted
aod: constant frequency idle frames
1 parent ef88e81 commit 2bb611d

2 files changed

Lines changed: 55 additions & 1 deletion

File tree

src/displayapp/DisplayApp.cpp

Lines changed: 48 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,36 @@ void DisplayApp::InitHw() {
154154
lcd.Init();
155155
}
156156

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

src/displayapp/DisplayApp.h

Lines changed: 7 additions & 0 deletions
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)