Pages: [1] 2   Go Down
Author Topic: Power Save Mode (Sleep)  (Read 4822 times)
0 Members and 1 Guest are viewing this topic.
0
Offline Offline
Edison Member
*
Karma: 16
Posts: 1579
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Hola,

He estado jugando un poco con sleep del ATMega168. En concreto con Power save Mode, para sustituir esto en mis delays y poder ahorrar bateria.

En concreto pongo el micro en Sleep y se despierta cuando hay un Overflow del Timer2.

Viendo resultados, merece la pena....

Pongo el código de una simple aplicación que hace parpadear el led del pin 13 del Diecimila durante 2 segundos (para poder medir la corriente).
Los resultados han sido:
-Sleep Mode and turn off devices=> Led_on=12,98 mA & Led_off=9,91 mA
-No Sleep and turn off devices   => Led_on=28,9 mA & Led_off=25,9 mA
-No Sleep                               => Led_on=32,2 mA & Led_off=29,2 mA

Turn off devices es que apago el ADC, Timer 0, Timer 1, USART, TWI y SPI.


SLEEP MODE TEST CODE

Code:
#include <avr/interrupt.h>
#include <avr/sleep.h>


volatile int cont;

void setup()
{
  pinMode(13,OUTPUT);
  
  TCCR2A=0;  
  TCCR2B=(0<<7)|(0<<6)|(0<<5)|(0<<4)|(0<<3)|(1<<2)|(1<<1)|(1<<0);
  // Bit 0.- Overflow
  TIMSK2=(0<<2)|(0<<1)|(1<<0);
  //Power Save Mode
  SMCR=(0<<3)|(1<<2)|(1<<1)|(0<<0);
    
}

ISR(TIMER2_OVF_vect)
{
  cont++;
}

void loop()
{

   sleep_enable();
   //Turn off ADC
   ADCSRA=0;
   // TWI | TIMER2 | TIMER0 | VOID | TIMER1 | SPI | USART | ADC
   PRR=(1<<7)|(0<<6)|(1<<5)|(1<<4)|(1<<3)|(1<<2)|(1<<1)|(1<<0);
   sei();
   while (cont<122)
   {
   //Go to Sleep
   sleep_mode();
   }
   //Disable Sleep
   sleep_disable();
   //Disable interrupts
   cli();
   cont=0;
   digitalWrite(13,!digitalRead(13));
  
  
}





NO SLEEP AND TURN OFF DEVICES

Code:
#include <avr/interrupt.h>
#include <avr/sleep.h>


volatile int cont;

void setup()
{
  pinMode(13,OUTPUT);
  
  TCCR2A=0;  
  TCCR2B=(0<<7)|(0<<6)|(0<<5)|(0<<4)|(0<<3)|(1<<2)|(1<<1)|(1<<0);
  // Bit 0.- Overflow
  TIMSK2=(0<<2)|(0<<1)|(1<<0);
  //Turn off ADC
  ADCSRA=0;
  // TWI | TIMER2 | TIMER0 | VOID | TIMER1 | SPI | USART | ADC
  PRR=(1<<7)|(0<<6)|(1<<5)|(1<<4)|(1<<3)|(1<<2)|(1<<1)|(1<<0);
  sei();
}

ISR(TIMER2_OVF_vect)
{
  cont++;
  if (cont<122)
  {
    cont=0;
    digitalWrite(13,!digitalRead(13));  
  }
    
}

void loop()
{

  
}




NO SLEEP

Code:
#include <avr/interrupt.h>
#include <avr/sleep.h>


volatile int cont;

void setup()
{
  pinMode(13,OUTPUT);
  
  TCCR2A=0;  
  TCCR2B=(0<<7)|(0<<6)|(0<<5)|(0<<4)|(0<<3)|(1<<2)|(1<<1)|(1<<0);
  // Bit 0.- Overflow
  TIMSK2=(0<<2)|(0<<1)|(1<<0);
  //Turn off ADC
  //ADCSRA=0;
  // TWI | TIMER2 | TIMER0 | VOID | TIMER1 | SPI | USART | ADC
  //PRR=(1<<7)|(0<<6)|(1<<5)|(1<<4)|(1<<3)|(1<<2)|(1<<1)|(1<<0);
  sei();
}

ISR(TIMER2_OVF_vect)
{
  cont++;
  if (cont==122)
  {
    cont=0;
    digitalWrite(13,!digitalRead(13));  
  }
    
}

void loop()
{

  
}


Por si alguien le pueda venir bien...


Salu2, smiley



Igor R.
Logged


0
Offline Offline
Edison Member
*
Karma: 16
Posts: 1579
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Hola,

Siguiendo con intentar bajar el consumo, he realizado otra prueba.
He quitado el led de power, y he probado otro regulador LP2951CN (Tiene un pin de shut down, asi puedo apagar mi aplicacion si hay un tiempo de inactividad).

Test de Power Sleep Mode:

    * Arduino con su propio regulador => 6'52 mA
    * Arduino con LP2951CN             => 1'54 mA

El regulador que hay con Diecimila consume 5mA en comparación con este nuevo.

Espero que alguien le pueda servir esta info.


Salu2   smiley-wink



Igor R.
Logged


0
Offline Offline
Edison Member
*
Karma: 16
Posts: 1579
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Se me olvido decir que hay que dejar libre el jumper que eliges donde viene la alimentación (USB ó EXT). Ya que si lo dejas en EXT y lo alimentas con otro regulador mediante el pin de 5 voltios y GND, el viejo consume corriente....
De esta forma, ya no le llega nada al regulador y no tienes que desoldar nada del Diecimila.

 smiley-wink
Logged


Chile
Offline Offline
Edison Member
*
Karma: 32
Posts: 1233
Arduino rocks?
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Cool!
Logged

My website: http://ried.cl

0
Offline Offline
Newbie
*
Karma: 0
Posts: 46
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Muy buenas. Retomando este magnifico post, he estado haciendo unas pruebas y no consigo lo que quiero. Mi intención es gastar la menor energia posible apagando todos los módulos que no utilizo y cuando la interrupcion del timer1 (uso el que me brinda mayor retardo posible dormido, 16 bits) despierte al micro, haga una serie de cosas, en este caso encender y apagar un led. Lo gracioso del asunto es que éste led se queda encendido, osea, como si se parase justamente en el delay que hay antes de apagarlo. Me gustaria alargar el sleep todo lo que se pueda hasta provocar la interrupcion pero no se si lo hago bien o si se puede alargar mas.

Cuelgo el codigo por si alguien es tan amable de echarle un vistazo:


#include <TimerOne.h>
#include <avr/sleep.h>

int led = 9;
int interled = 13;
int state = LOW;
int contador = 0;

void setup() {
  pinMode(led, OUTPUT);
  pinMode(interled, OUTPUT);

  Timer1.initialize();   // Initializes Timer1 (Period)
  Timer1.attachInterrupt(flash, 2388480);
}

void loop() {
    digitalWrite(led, HIGH);  
    delay (100);
    digitalWrite(led, LOW);  
    delay (100);    

    set_sleep_mode(SLEEP_MODE_IDLE);   // sleep mode is set here
    
 
  //Turn off ADC
  ADCSRA=0;
      // TWI | TIMER2 | TIMER0 | VOID | TIMER1 | SPI | USART | ADC
   PRR=(1<<7)| (1<<6) | (1<<5) |(1<<4)| (0<<3) |(1<<2)|(1<<1)|(1<<0);
   sei();
    
    sleep_enable();          // enables the sleep bit in the mcucr register
                             // so sleep is possible. just a safety pin
    sleep_mode();            // here the device is actually put to sleep!!
                             // THE PROGRAM CONTINUES FROM HERE AFTER WAKING UP
   sleep_disable();

}
void flash()
{
    state = !state;
    digitalWrite(interled, state);  
}


Mil gracias y saludos.
Logged

0
Offline Offline
Edison Member
*
Karma: 16
Posts: 1579
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Hola,

No me he mirado en profundidad tu código porque desconozco la libreria TimerOne.
Yo en estos casos prefiero actuar directamente sobre los registros del micro.
Tambien desconozco si el Timer 1 sirve para despertar el micro en Idle.
Aunque si todo funcionase bien, lo que haces en la función del Timer 1(flash) es cambiar de estado el pin 13, pero resulta que en el void loop, lo dejas apagado. Ten en cuenta que cuando se despierte hará la función flash y luego continuará en el punto anterior donde se durmió... (despues de sleep_mode(), por lo que volverá a encender y apagar el led).Ya que es un bucle que volverá a enceder-retardo-apagar y otra vez en sleep.

En el ejemplo que puse, si te fijas, aunque usa el Timer2 que es de 8 bits, cuando se despierta lo que hace es incrementar una variable (cont) y si no es mayor de 122, lo vuelve a dormir.... más o menos era para dejarlo 2 segundos dormido (es decir, 122 veces Overflow del Timer 2). Puedes aumentar hasta lo que quieras.

Si no me equivoco Save Mode consume menos que Idle. Yo apagaría antes de entrar en Sleep, los módulos que no quieras usar.

Lo que haría es sacarme los registros principales que se usan para los Sleep Modes por el puerto serie para poder monitorizar los valores y estar seguro que todo esta bien.


Salu2


Igor R.
« Last Edit: July 07, 2009, 01:53:31 pm by igorreal » Logged


0
Offline Offline
Newbie
*
Karma: 0
Posts: 46
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Hola. Ante todo gracias por tu respuesta. El led no lo apago en bucle loop puesto que utilizo dos leds, en el bucle loop enciendo y apago uno, para saber que se ha despertado y en la rutina flash cambio de estado el otro cada vez que desborda el timer. El timer2 si lo he utilizado, pero su temporización es muy pequeña y quiero dormir al micro unos minutos sin que se despierte muchas veces por minuto porque al final me va gastando la bateria de una forma un poco inutil. Si que habia pensado hacer lo del contador que me comentas pero con un sleep largo para poder llegar a varios minutos.
La libreria TimerOne se utiliza más que nada para las salidas PWM, pero yo queria utilizar el timer de 16 bits con interrupciones y soy novato en arduino (estoy acostumbrado a los PIC).
Si que en save mode consume menos, pero me apaga el timer1, y no se si se puede utilizar el save mode y activar tambien el timer1.
Intentare sacar los valores por puerto serie como me dices para visualizarlos mejor.

Gracias de nuevo y un saludo.
Logged

0
Offline Offline
Edison Member
*
Karma: 16
Posts: 1579
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Perdón,no me habia fijado bien que tenias dos leds...

Te digo lo de los registros, porque cuando compilas, te introduce código de Arduino(wiring) que por ejemplo modifica el Timer 1 (ya que se usan en las funciones de pwm). Aunque se supone que esto lo hace antes que setup().

Aunque uses el Timer 1 de 16 bits, si no me equivoco, tendras un overflow cada 4,2 segudos aproximadamente teniendo el  prescaler máximo (1024) y el reloj de 16 Mhz. Por lo que minutos tampoco puedes conseguir... Tendrás que usar un contador para ampliar tiempo. En la libreria dicen que son 8,3 segundos, pero a mi no me salen las cuentas, a no ser que el clock sea de 8 Mhz.

En Power Save sólo el Timer 2 puede despertar el micro.

Yo cambiaría el código para manipular directamente los registros en vez de usar la libreria Timer1 y manipules directamente la interrupción del Timer 1. Lo digo para que tengas control total... http://www.avrfreaks.net/index.php?name=PNphpBB2&file=viewtopic&t=50106

Es tan sólo modificar lo que hice yo con los registros del Timer 1, ISR(TIMER1_OVF_vect)
 y cambiar el modo de sleep a Idle.

Ya nos contarás tus avances.... smiley-wink


Saludos



Igor R.





Logged


0
Offline Offline
Edison Member
*
Karma: 16
Posts: 1579
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Acabo de ver en la documentacion de Atmel que en Phase and Frequency Correct sube hasta Top y Baja, por lo que es el doble....TOV1 Flag se activa en Bottom.
En la libreria Timer 1:
WGM13 esta a 1.
WGM12-10 esta a 0.
Por lo que Top lo marca ICR1, el update de OCR1x es abajo y TOV1 Flag es en abajo.
Tabla 15-4 de la página 133 de la documentacion de ATmega168.

Por esto lo de los 8,4 segundos....
Por lo que lo máximo que pudes obtener son 8.38 segundos


Salu2


Igor R.


« Last Edit: July 08, 2009, 04:42:09 am by igorreal » Logged


0
Offline Offline
Newbie
*
Karma: 0
Posts: 46
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Gracias por tus consejos. Voy a intentar mezclar la libreria con lo que me has enseñado, a ver si consigo llegar a algo más concreto.

Un saludo.
Logged

0
Offline Offline
Newbie
*
Karma: 0
Posts: 46
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Hola de nuevo. Pues sigo trabado, ahora he cambiado el parpadear un led en el loop por mandar un mensaje serie, pero solo me funciona una vez, ya lo mande al principio o al final del loop, es como si solamente hiciese el loop una vez y luego se quedase trabado...


#include <TimerOne.h>
#include <avr/sleep.h>

int led = 9;
int interled = 13;
int state = LOW;
int contador = 0;

void setup() {
    Serial.begin(9600); // open serial
  pinMode(led, OUTPUT);
  pinMode(interled, OUTPUT);

  Timer1.initialize();   // Initializes Timer1 (Period)
  Timer1.attachInterrupt(flash, 2388480);
}

void loop() {
 
               Serial.print("BUCLE ");
    set_sleep_mode(SLEEP_MODE_IDLE);   // sleep mode is set here
    
  //Turn off ADC
  ADCSRA=0;
      // TWI | TIMER2 | TIMER0 | VOID | TIMER1 | SPI | USART | ADC
   PRR=(1<<7)| (1<<6) | (1<<5) |(1<<4)| (0<<3) |(1<<2)|(1<<1)|(1<<0);
        
   sei();  // habilita interrupciones

   //Go to Sleep
   sleep_mode();
 
   //Disable Sleep
   sleep_disable();
   //Disable interrupts
   cli();    // deshabilita interrupciones


}
void flash()
{
    state = !state;
    digitalWrite(interled, state);  
}
Logged

0
Offline Offline
Newbie
*
Karma: 0
Posts: 46
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Hola otra vez. He simplificado un poco el codigo para que se vea mas claro. Ahora lo que ocurre es que no se duerme con el modo idle, me manda continuamente por el puerto serie cada 500ms en vez de despertarse con la interrupcion y sacarlo cada 2 segundos. Si le cambio el modo sleep a uno mas potente el programa se detiene despues de enviar un solo mensaje serie, osea, que se duerme pero no sale del reposo por las interrupciones del timer1. He probado a activar todos los modulos en el registro PRR pero sigue igual. Creo que solamente con conseguir despertarlo del sleep podria ser suficiente... He leido algo sobre poner el timer2 en modo asincrono, asi que me pondré con ello.

Saludos!!

#include <TimerOne.h>
#include <avr/sleep.h>

int interled = 13;
int state = LOW;

void setup() {
   Serial.begin(9600); // open serial
 pinMode(interled, OUTPUT);

 Timer1.initialize();   // Initializes Timer1 (Period)
 Timer1.attachInterrupt(flash, 2000000);      // 8388480 maximo
 
    set_sleep_mode(SLEEP_MODE_PWR_SAVE);   // sleep mode is set here
    
}

void loop() {

              Serial.print("BUCLE");

  //Go to Sleep
  sleep_mode();

  //Disable Sleep
  sleep_disable();

  delay(500);
}
void flash()
{
   state = !state;
   digitalWrite(interled, state);  
}
Logged

0
Offline Offline
Edison Member
*
Karma: 16
Posts: 1579
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Cuando me referia a monitorizar por el puerto serie los registros, me refiero a que mirando la documentación de atmel, te fijes en los registros que se deben configurar y monitorices su estado.
Como por ejemplo los registros son: TCCR1A, TCCR1B, TIMSK1,SMCR, ...

¿Has probado el programa sin el sleep? ¿Es decir, el programa funciona saltando a la interrupción?

 smiley-wink
Logged


0
Offline Offline
Newbie
*
Karma: 0
Posts: 46
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Muy buenas. Si, el programa funciona bien sin el sleep o poniendolo en modo IDLE, pero es como si el modo idle no le dejase dormido, ya que hace la rutina del loop constantemente, no espera a despertarse por la interrupcion del timer1, y si pongo otro modo de sleep se duerme pero no es capaz de despertar con la interrupcion. Voy a visualizar los registros con sleep y sin sleep a ver si viendo las diferencias llegamos a alguna conclusion.

Gracias por tu ayuda!!!   smiley
Logged

0
Offline Offline
Newbie
*
Karma: 0
Posts: 46
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Buenos dias. He intentado realizar los protocolos que vienen en el datasheet para establecer el timer2 como asincrono y asi poder usar el PWR_SAVE, pero tampoco funciona.... Lo de enviar los datos por puerto serie no es complicado, y puedo ver que efectivamente los valores son los que he metido. Lo que si es interesante es ver que si pongo un delay antes de sacar los valores por el puerto serie se queda bloqueado, porque no me saca nada por el puerto serie, aunque el delay se de 1ms...

#include <TimerOne.h>
#include <avr/sleep.h>

int interled = 13;
int state = LOW;

void setup() {
  Serial.begin(9600); // open serial
  pinMode(interled, OUTPUT);

  Timer1.initialize();   // Initializes Timer1 (Period)
  Timer1.attachInterrupt(flash, 2000000);      // 8388480 maximo
  
  TCCR1A=0;
  TCCR1B=5;
  TCCR1C=0;
  TCNT1H=0;
  TCNT1L=0;
  TIMSK1=1;
  TIFR1=0;
  
  TCCR2A=0;
  TCCR2B=7;
  TIFR2=0;
  ASSR=0;
  GTCCR=0;  
  TIMSK2=0;    // borramos los bits OCIE2x y TOIE2
  ASSR=(0<<5);  // bit AS2 a 0
  TCNT2=0;    // escribimos algo en estos registros
  OCR2A=0;
  OCR2B=0;
  delay(2); // testeamos que el bit busy del ASSR esté a 0 (lo hacemos con un delay y listo)
  TIMSK2=0;    // borramos los bits OCIE2x y TOIE2

    
    ADCSRA=0;
  // TWI | TIMER2 | TIMER0 | VOID | TIMER1 | SPI | USART | ADC
  PRR=(1<<7)| (0<<6) | (1<<5) | (0<<3) |(1<<2)|(0<<1)|(1<<0);

  set_sleep_mode(SLEEP_MODE_PWR_SAVE);   // sleep mode is set here

}

void loop() {


  Serial.println();
  Serial.print("TCCR1A ");
  Serial.println(TCCR1A, HEX);
  Serial.print("TCCR1B ");
  Serial.println(TCCR1B, HEX);
  Serial.print("TCCR1C ");
  Serial.println(TCCR1C, HEX);  
  Serial.print("TIMSK1 ");
  Serial.println(TIMSK1, HEX);    
  Serial.print("TIFR1 ");
  Serial.println(TIFR1, HEX);    
  Serial.print("TCCR2A ");  
  Serial.println(TCCR2A, HEX);
  Serial.print("TCCR2B ");
  Serial.println(TCCR2B, HEX);
  Serial.print("TIMSK2 ");
  Serial.println(TIMSK2, HEX);    
  Serial.print("TIFR2 ");
  Serial.println(TIFR2, HEX);  
  Serial.print("ASSR ");
  Serial.println(ASSR, HEX);  
  Serial.print("GTCCR ");
  Serial.println(GTCCR, HEX);  
  Serial.print("SMCR ");  
  Serial.print(SMCR, HEX);    

  delay(500);
  //Turn off ADC

  sei();  // habilita interrupciones

    TCCR2A=0;    // escribimos algo segun el protocolo
    delay(2);    // testeamos que el bit busy del ASSR esté a 0 (lo hacemos con un delay y listo)
  sleep_mode();  // entramos en sleep

  //Disable Sleep
  sleep_disable();
  //Disable interrupts
    cli();    // deshabilita interrupciones

  delay(500);
}
void flash()
{
  state = !state;
  digitalWrite(interled, state);  
}


« Last Edit: July 09, 2009, 08:54:47 am by Pegasux » Logged

Pages: [1] 2   Go Up
Jump to: