Sleep y WakeUp con PIC

Esta nota sera de utilidad para trabajar el bajo consumo de un microcontrolador PIC.
A menudo tenemos aplicaciones donde se requiere trabajar en bajo consumo, aplicaciones que utilizan pilas, donde queremos que la autonomía sea lo suficiente para que el dispositivo no requiera de un cambio de pilas frecuente, o bien funcione con energía alternativa (Solar, Piezoelectrica, Harvesting, etc...), o también para dispositivos descartables.
Aprovechando la tecnología nanoWatt de Microchip, vamos a implementar un pequeño ejemplo donde mostraremos como dormir y despertar el microcontrolador para realizar alguna pequeña tarea.
Por ejemplo, el microcontrolador PIC16F676 (un microcontrolador de bajo costo y bajas prestaciones) posee tecnología nanoWatt que tendremos que ver en su datasheet que valores de consumo maneja según la configuración del clock que utiliza.
En la figura de arriba, podemos ver que el datasheet nos proporciona distintas características para trabajar en baja potencia.
Analizaremos cada una de ellas:

  • Standby Current: Este es un estado donde el microcontrolador se encuentra en modo Sleep "dormido" y la única manera de realizar un WakeUp "despertar" es mediante una interrupción externa (por ejemplo cambio de estado en un GPIO), Este estado tiene un consumo de tan solo 1nA si el microcontrolador se alimenta a 2V, pero estamos hablando de 1nA es realmente muy bajo consumo.
  • Operating Current: Este estado es el modo común de operación donde no utilizamos ningún bajo consumo, solamente nos detalla el consumo si trabajamos a 32kHz alimentado con 2V donde el consumo sera de 8.5uA, o bien, si trabajamos a 1MHz sera de 100uA, pero esto hay que tener cuidado que por cada MHz de clock sera 100uA de consumo, ejemplo para 20MHz tendremos 2mA.
  • Watchdog Timer Current: Este metodo es el que emplearemos en el ejemplo, es el método que solamente tiene corriendo o ejecutando el timer del WDT, es decir, en el primer caso el microcontrolador se encuentra totalmente congelado, sin realizar ninguna acción hasta que aparece una interrupción externa por hardware, en este caso requiere que el timer WDT se encuentre activo y contando. Para este contador WDT el consumo es de 300nA a 2V
  • Timer 1 Oscillator Currnet: Este estado es igual al del WDT pero en lugar de mantener el timer WDT activo, mantiene solo el Timer1, que posee un consumo mayor al WDT de al menos 4uA a 32kHz, siempre alimentado con 2V.

En el caso del contador WDT "WatchDogTimer" tenemos que hablar primero de que es el WDT.
El timer WatchDog, o también conocido como el temporizador de perro guardián, es un timer creado para realizar un reset al microcontrolador cuando el mismo desborda su cuenta, es decir, el timer WDT comienza a contar, cuando el mismo llega al valor máximo y se desborda produce un reset que reinicia el microcontrolador.
Para evitar este reinicio podemos resetear el WDT para que nunca llegue a desbordarse y nos reinicie el microcontrolador.
Este proceso es útil cuando nuestro programa podría entrar en un loop o bucle donde quede colgado y no pueda salir.
Es decir, si nosotros tenemos un programa donde algunas condiciones del mismo llevan a que se cuelgue (queda en loop cerrado en alguna instrucción y no puede salir) debemos reiniciar el microcontrolador mediante el botón de reset.
Pero si en cambio utilizamos el WDT, podemos configurarlo para que el WDT produzca un reinicio del microcontrolador cada determinado tiempo, por ejemplo 1000ms, entonces en distintos sectores del programa pondremos la instrucción que borra el WDT, para que borre la cuenta y no llegue a contar esos 1000ms y no reinicie el MCU, pero si nuestro programa queda colgado en Loop cerrado dentro de una rutina y nunca llega a borrar la cuenta del WDT, el mismo seguirá contando y cuando llegue a 1000ms reiniciara el MCU. 
De ahi el nombre de perro guardián, porque esta todo el tiempo mirando que no se cuelgue el programa, y si eso pasara, reiniciaría el MCU.

Bueno hasta aquí, no hablamos de bajo consumo.
Entonces lo que nosotros debemos utilizar es una función que duerme al microcontrolador, esta función se denomina SLEEP, la misma duerme el microcontrolador y solamente lo podemos despertar por Reset o alguna interrupción externa.
Entonces, si nosotros mandamos a dormir al microcontrolador con SLEEP y utilizamos el WDT por ejemplo a 2304ms (sin borrar su cuenta en ningún lado porque el MCU se encuentra en SLEEP y no ejecuta programa), entonces tendremos un RESET programado cada 2304ms, ese reset nos despertara el microcntrolador "WakeUp" y podremos hacer la tarea que queremos y luego de hacer la tarea, mandamos a dormir al microcontrolador con "sleep".

Entonces, 

  1. Activamos los fuses del WDT
  2. Configuramos el WDT a 2304ms (es un valor raro, pero es el valor múltiplo del oscilador, el prescaler, etc...)
  3. Realizamos nuestro programa
  4. Incluimos el Sleep al finalizar la rutina cuando queremos que duerma.

  1. #include <16F676.h>
  2. #FUSES WDT
  3. #FUSES INTRC_IO
  4. #FUSES NOMCLR
  5. #use delay(int=4000000,RESTART_WDT)
  6. #define LED PIN_A0
  7. void main(){
  8.    setup_timer_0(RTCC_INTERNAL|RTCC_DIV_1);
  9.    setup_wdt(WDT_2304MS);
  10.    while(true){
  11.       output_low(LED);
  12.       delay_ms(200);
  13.       output_high(LED);
  14.       delay_ms(200);
  15.       sleep();
  16.    }
  17. }

#FUSES WDT
Este es el fuse que debe utilizarse para activar el WDT

#use delay(int=4000000,RESTART_WDT)
Dentro del delay, donde configuramos el clock, tendremos que agregar RESTART_WDT

setup_wdt(WDT_2304MS);
En la configuración del MCU dentro de la función principal "main" debemos configurar el mismo, siempre con el mismo intervalo.

sleep();
Esta es la funcion SLEEP para dormir al MCU, luego de realizar la tarea.

Nuestro programa prende y apaga un LED cada 2,3s

Entonces cuando se encuentra en sleep, el MCU pasara a 300nA (si se encuentra alimentado a 2V) durante 2304ms, y luego cuando se despierta eleva el consumo, realiza la tarea y vuelve a dormir. entonces en cuanto a la energía consumida, podemos obtener un bajo consumo (siempre que el tiempo en Sleep sea mayor que el tiempo activo) y aumentar la autonomía de las pilas o la fuente que lo alimenta.

El datasheet también menciona que los GPIO que no se utilizan, deben ser ruteados a GND en nuestro PCB para reducir el consumo y lograr los 300nA.

Por ejemplo en el gráfico, si tenemos un bloque RUN de 10ms y 1mA, y un bloque SLEEP de 2304ms y 300nA, podemos calcular el consumo promedio de 4.62uA que para una pila tipo CR2032 de 200mA, nos proporciona 4 Años, 343 dias, 15Hs, 56 min.
Tranquilamente podria ser un sistema descartable como los que se acostumbran hoy con los beacon BLE, LoRa, Zigbee, etc...