[RFC] Reduce CPU usage when idle

I think the API of lv_task_handler can be improved to reduce CPU usage when nothing is happening.

Currently, we recommend that users call it every 10 milliseconds or so. If there is no work to be done, it will return almost immediately, and the system can go back to sleep for another 10 milliseconds.

These constant wakeups do affect power consumption, though, and we have enough information stored within the task structure to be able to handle things much more efficiently.

My proposal consists of a few different improvements which should enable us to reach 0% CPU usage when idle. All of these can be done in a minor release as they won’t break existing functionality.

I’m happy and eager to implement these myself; this post is mainly to confirm that others (particularly @kisvegabor) agree with the general strategy.

  1. Make lv_task_handler return the time in milliseconds after which another task will be ready to run. This will enable the OS to sleep for exactly the required amount of time (instead of picking an interval like 10ms). If nothing needs to be done then a special value should be returned that indicates LittlevGL is waiting for some event to occur (i.e. input or a widget state change).
  2. In order to achieve 0% CPU usage, we must ensure that no LittlevGL task is “ready-to-run” (in OS terminology) if no work needs to be done. We should make use of LV_TASK_PRIO_OFF for built-in tasks.
  3. lv_disp_refr_task should be modified to change its priority to LV_TASK_PRIO_OFF once it finishes refreshing all invalidated areas. Any function which invalidates areas should then resume the refresh task. This ensures that the refresh task only runs if something actually needs to be drawn.
  4. We need to add a mode whereby the input task can be suspended at initialization time and triggered to run when necessary (e.g. by a touch interrupt). Currently, like the refresh task, the input task runs on a set frequency even if no input is available.

With these changes, it should be possible to completely suspend the processor if nothing has changed within LittlevGL.

Any thoughts?

I’ve started implementing this in https://github.com/littlevgl/lvgl/pull/1309.

1 Like

Supporting this (from the user side) would require more effort when integrating lvgl into a system.
So I think this feature should be optional, and only enabled if users opt in.
I don’t think this should be the default.

Current way of periodically calling lv_task_handler and the fact that no interrupt handling is required for the input device make things very easy, and the additional power consumption required for polling every 10 milliseconds might be negligible at least for some applications.

Again, this would be implemented transparently. Existing applications would continue to work as before. This just provides another route for optimization.

I’m thinking that the input device change would require users to set a flag that switches the device to interrupt-driven mode. After that, they would simply be expected to start calling lv_task_handler again if they received any input.

Those who don’t set the flag would continue to get the current behavior, i.e. polling every 30ms (or whatever is set in lv_conf.h).

In one of my projects, I need to optimize the power consumption, so (as a real-life example) let’s see how this feature could help there. I work with an ST MCU and I use a Touchpad, USB and UART as external sources.
I suppose any of these sources can generate interrupts which can wake up the MCU . We could disable the indev handling with lv_indev_enable(tp_indev, false) and enable it again when a TP IRQ arrives.
So all seems fine form this side, the only issue is the ST’s Tick timer which causes interrupts in every millisecond. So for example

  • if no TP IRQ for 100 ms disable that indev
  • if no lv_task to execute, disable the Tick timer and go to sleep
  • wait until TP, USB or UART wakes up MCU
  • enable indev
  • let lv_task_handler run

So it seems doable for me.

I made a quick calculation to see how much we could win:

  • let’s say with the current version the MCU is awake in 1/10 of the time to handle empty lv_tasks, and the Tick timer
  • I’m not sure about the exact power consumption but it should be somewhat ~100 uA in sleep and 50 mA awake. So the total consumption is 0.9 * 100 uA + 0.1 * 50 mA = ~5mA compared to 100 uA while sleeping continuously. These numbers are only wild guesses but as there is a huge difference in the run/sleep current every millisecond awake should make a big difference.

Any particular reason why you would want to wait 100 ms, and not just disable the TP immediately, re-enabling it only when an interrupt arrives with touch data? That way only the required tasks run (i.e. there’s no need to call indev_read when there is no data)?

I really havn’t explained the reason. It’s because my TP controller sends interrupt pulses every few milliseconds. So it’s special to my case but doesn’t really matter in general.

1 Like

In your project, when the device is in sleep mode, is the screen still on? If it is, wouldn’t the screen back-light and controller consume so much power anyway, such that the gain from this feature is negligible?

Actually it’s also true. The MCU is about 50 mA and the rest (display, backlight, DC converters, etc) are another 50 mA. So it’s about 55 mA vs 50 mA which is really not too much.

Anyway, if it’s transparent for the user, why not add this opportunity even if it has only a minor gain?

Complexity has its price. Development effort, bugs, code size, maintenance, documentation, educate the users about this mode of operations and answer their questions, user API is a bit more complex etc. Today lvgl has a very simple API for display and input devices - and it’s a plus.

On the other hand, I can think of one application where this might be useful: - epaper displays. These displays have no back-light and only consume power when the display changes. I’m not sure how many people (if anyone at all) are using lvgl with epaper, and whether this effort is worth for such a corner application.

I think this is highly exaggerated.
Did you measure 1/10 of the time for empty lv_tasks and tick?? I would say more like 1/100 or even less! Worth checking this.

Nice list :slightly_smiling_face:
Let’s see if we find good use cases to decide whether it’s worth the effort.

E-paper is a good use case. I remember that @embeddedt made some experience with e-paper about a year ago.

Another thing, what if the display is turned off (similarly to smartphones)? In this case, this feature would be very important.

Probably you are right.

True, but the advantage here is that we can implement this without having to change the existing simplicity. Users only have to opt-in to the additional complexity if they so desire.

The numbers we used probably aren’t accurate, but the point remains that the MCU has to keep waking up, and that can have an effect on power.

All I’m saying is that I think you should first do some basic calculations and measurements.
If, for example, eventually you save maximum of 0.1% power, would you still do it?

I am doing some tests for a project of mine, and I’d like the LittlevGL thread to be able to suspend completely and not have to wake up. I figured that I might as well contribute the changes since they wouldn’t affect existing applications. I think the power savings would depend on the exact use case; this just enables the possibility. Before it would be impossible to easily suspend.

Considering the case I’ve mentioned this feature is clearly reasonable:


Has the touch interrupt already been implemented in the littlevgl library ?

I would like to use interrupt of FT6236 touch controller to wake up only the display or wake up all the system or to make lib littlevgl read the touch only when the display controller sends an interrupt signal.


I would really like this feature.

I am still working on this. I’ll keep you updated.

1 Like