Power and cooling
There is no fan. Cooling means a slower clock and a dimmer backlight. Saving power uses the same two controls, plus a light-sleep recommendation when nothing is runnable.
kernel/power_governor.c is the only policy that changes them. Apps do not
pick a megahertz. They publish how much compute they want, and the governor
treats that as a ceiling.
Levels
The governor ranks whatever frequencies power_get_available_freqs()
returns. It does not hardcode 240 MHz. On the host those are 240, 160, 80,
40, and 10. The ESP32-C6 boot path installs 160, 80, 40, 20, and 10.
| Level | When | Clock | Backlight cap |
|---|---|---|---|
| Performance | A short burst, or an app that asked for HIGH |
Highest | 255 |
| Balanced | Default interactive app | Middle | 180 |
| Economy | Idle, or the app asked for little work | Second lowest | 64 |
| Cool | The die is hot, or a task is burning CPU without having asked for HIGH |
Lowest | 16 |
One busy window steps up, so a tap still feels quick. Several quiet
windows step down, so the clock does not flicker. A sustained full load
with a hint below HIGH drops one level and stays there until the machine
goes quiet. HIGH may keep the top clock.
Temperature, when a sensor answers, overrides everything. At 70 °C the level
is Cool, including a refused HIGH request. It stays there until the die
falls to 60 °C. No sample means the thermal rule stays off. The simulator
has no die sensor.
What apps publish
APP_HELPER(clock_app, "clock",
.demand = POWER_DEMAND_LOW,
.every_ms = 100,
.on_view = on_view);
| Hint | Meaning |
|---|---|
POWER_DEMAND_LOW |
Timer, clock, sensor list. Cannot sit on the top clock. |
POWER_DEMAND_NORMAL |
Default interactive app. |
POWER_DEMAND_HIGH |
A game that may use the top clock until the die is hot. |
POWER_DEMAND_IDLE |
Nothing runnable should be doing work. |
app_kit_set_demand() is the same hint for a screen that does not use
APP_HELPER. Leaving the foreground puts that app's live hint back to
NORMAL, so a game in the background does not keep the fast clock. Pomodoro,
clock, and sensors publish LOW on the app task. A worker that must stay
quiet calls power_set_demand() itself.
scheduler_step() never runs idle_task(). A step that finds only the idle
task is an idle sample. scheduler_tick() closes a 100 ms window and calls
power_governor_tick().
Light sleep, not deep sleep
When the level is Economy or Cool, nothing is runnable, and the next wake is
at least 50 ms away, power_governor_light_sleep_ms() returns that gap. The
governor does not call power_light_sleep(). On the host that function
blocks on the wall clock. Deep sleep stays an explicit power_deep_sleep()
call. It drops RAM and is still outside this policy.
Actuators
Tests install fakes with power_governor_set_actuators(). A NULL frequency
callback uses power_set_cpu_freq(). A NULL temperature callback leaves the
thermal rule off.
The simulator registers hal_display_set_backlight_cap(). That cap clamps
later hal_display_set_brightness() calls. The ESP32-C6 backlight is on/off,
so a non-zero cap leaves the panel on and only a zero cap blanks it.
ESP32-C6 boot (boards/esp32-c6-lcd/src_kernel/kernel_boot.cpp) registers
hal_power_set_cpu_freq() and reads the die through the temp sensor the
board config already owns. hal_power_get_die_temp_c() is the same reading
for a caller that does not go through the sensor service. AVR and ESP8266
report no die temperature.