El Power Management de Zephyr es el conjunto de mecanismos que coordina los estados de energía de la CPU y los dispositivos cuando el sistema no tiene trabajo inmediato.
Si dejas tu “Blinky” corriendo tal cual lo hemos programado hasta ahora, probablemente la batería se agote en un par de días. ¿Por qué? Porque el microcontrolador, aunque no esté haciendo “nada”, sigue encendido, consumiendo corriente.
Para esto utilizamos el subsistema Power Management (PM) de Zephyr, preparado para coordinar distintos niveles de bajo consumo.
El secreto: tickless idle
En un RTOS antiguo (como las versiones viejas de FreeRTOS), existe un “Tick” del sistema. Cada milisegundo, una interrupción despierta a la CPU para ver si hay que cambiar de hilo. Esto impide que el procesador entre en sueño profundo, porque lo estamos “molestando” constantemente.
Zephyr utiliza una arquitectura Tickless Kernel (Kernel sin Ticks).
- Cuando llamáis a
k_msleep(1000), el Kernel sabe que no tiene nada que hacer durante los próximos 1000ms. - En lugar de despertar cada 1ms para comprobar la hora, programa un timer de hardware para que suene exactamente dentro de 1000ms.
- Inmediatamente después, apaga la CPU y se va a dormir.
El kernel puede dejar la CPU en idle cuando los hilos están bloqueados. Entrar en estados más profundos requiere soporte de la plataforma, una política de energía y la configuración correspondiente; no depende únicamente de llamar a k_sleep.
Estados de energía (PM states)
No es lo mismo echarse una siesta de 5 minutos que dormir 8 horas. Los microcontroladores tienen diferentes niveles de sueño:
PM_STATE_ACTIVE: El sistema está activo.PM_STATE_RUNTIME_IDLE: Estado ligero de inactividad, si la plataforma lo implementa.PM_STATE_SUSPEND_TO_IDLEyPM_STATE_STANDBY: Conservan distinto contexto y ofrecen latencias diferentes según el SoC.PM_STATE_SUSPEND_TO_RAMyPM_STATE_SOFT_OFF: Estados más profundos, con fuentes de despertar y retención dependientes del hardware.
Zephyr abstrae esto en PM_STATES. Cuando el sistema detecta que el “Hilo Idle” (el hilo que corre cuando nadie más trabaja) está activo, consulta al Power Manager qué estado de sueño debe elegir basándose en cuánto tiempo tenemos libre.
- Si tenemos 100µs libres -> Idle.
- Si tenemos 1000ms libres -> Suspend.
Configuración básica
Para activar esta maquinaria, necesitamos tocar nuestro prj.conf.
# Habilitar el subsistema de gestión de energía
CONFIG_PM=y
# Habilitar la gestión de energía de dispositivos
CONFIG_PM_DEVICE=ySolo con CONFIG_PM=y, el sistema ya intentará entrar en estados de bajo consumo cuando todos los hilos estén dormidos.
Device power management: los periféricos
Aquí está la trampa. Puedes tener la CPU dormida y, aun así, agotar la batería si dejas encendido un receptor GNSS o un módulo Wi-Fi.
Zephyr tiene un sistema centralizado para decirles a los periféricos: “Oye, me voy a dormir, apágate tú también”.
Cuando el sistema entra en suspensión, Zephyr recorre la lista de dispositivos activos y llama a su función de PM.
Gestión de energía en tiempo de ejecución
Con CONFIG_PM_DEVICE_RUNTIME=y, los drivers compatibles pueden solicitar el dispositivo cuando lo necesitan y liberarlo después. El contador de uso decide cuándo reanudarlo o suspenderlo.
También se puede habilitar automáticamente para un nodo compatible:
&sensor0 {
zephyr,pm-device-runtime-auto;
};Las llamadas pm_device_runtime_get() y pm_device_runtime_put() corresponden normalmente al driver que conoce el ciclo de la operación, no a la aplicación. Si un driver no implementa PM, activar las opciones no apagará por sí solo el periférico.
Para que esto funcione, el driver del dispositivo debe soportar Power Management. La mayoría de drivers nativos de Zephyr (especialmente los de Nordic y NXP) lo soportan muy bien.
El problema del depurador (debugger)
Algunos estados profundos detienen los relojes o dominios que necesita la interfaz de depuración. La conexión SWD/JTAG puede perderse, aunque el comportamiento depende del SoC, la sonda y el estado seleccionado.
Es muy frustrante. Intentas depurar por qué tu código falla, pero en cuanto llega a k_sleep, el IDE te dice “Target disconnected”.
Para desarrollar, a menudo tenemos que desactivar el PM temporalmente o decirle que no use los estados más profundos.
En prj.conf para depuración:
CONFIG_PM=n # Desactivar PM mientras desarrollamos la lógicaO en código, podemos bloquear temporalmente un estado concreto. Si tu placa ofrece varios estados profundos, tendrás que adquirir un bloqueo para cada uno de los que quieras impedir:
#include <zephyr/pm/policy.h>
int main(void) {
/* Bloqueamos SUSPEND_TO_IDLE mientras depuramos */
pm_policy_state_lock_get(PM_STATE_SUSPEND_TO_IDLE, PM_ALL_SUBSTATES);
/* ... código a depurar ... */
/* Al terminar, permitimos de nuevo este estado */
pm_policy_state_lock_put(PM_STATE_SUSPEND_TO_IDLE, PM_ALL_SUBSTATES);
return 0;
}