@@ -153,6 +153,36 @@ void DisplayApp::InitHw() {
153
153
lcd.Init ();
154
154
}
155
155
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
+
156
186
void DisplayApp::Refresh () {
157
187
auto LoadPreviousScreen = [this ]() {
158
188
FullRefreshDirections returnDirection;
@@ -203,7 +233,21 @@ void DisplayApp::Refresh() {
203
233
switch (state) {
204
234
case States::Idle:
205
235
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
+ }
207
251
} else {
208
252
queueTimeout = portMAX_DELAY;
209
253
}
@@ -246,6 +290,9 @@ void DisplayApp::Refresh() {
246
290
if (settingsController.GetAlwaysOnDisplay ()) {
247
291
brightnessController.Set (Controllers::BrightnessController::Levels::AlwaysOn);
248
292
lcd.LowPowerOn ();
293
+ // Record idle entry time
294
+ alwaysOnTickCount = 0 ;
295
+ alwaysOnStartTime = xTaskGetTickCount ();
249
296
} else {
250
297
brightnessController.Set (Controllers::BrightnessController::Levels::Off);
251
298
lcd.Sleep ();
0 commit comments