@@ -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+
157187void 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 ();
0 commit comments