Estoy tratando de reducir el consumo de corriente de mi Atmega328 -Arduino- usando los modos de suspensión. En mi código, quiero despertar cada 10ms
, leer el valor de ADC y volver al modo de suspensión.
Hay algunas instrucciones útiles ( aquí y aquí ), pero la mayoría de ellas se basan en Watch Dog Timre o en interrupciones externas de pines.
Aquí está el código simplificado mío, pero no funciona (no va a la función de controlador de interrupciones):
En la configuración, me ocupo del temporizador y su controlador:
void setup() {
Serial.begin(19200);
Timer1.initialize(10000); //10ms sampling rate
Timer1.attachInterrupt(ReadMyADC,10000);
power_spi_disable(); // SPI
power_timer0_disable();// Timer 0
}
El resto del código incluido loop()
es el siguiente:
void ReadMyADC(){
sleep_disable();
PRR = PRR & 0b00000000;
Serial.print("sth");
power_adc_enable(); // ebable ADC converter
//Read ADC
power_adc_disable();
}
void loop() {
// ----------------low power ---------------
PRR = PRR | 0b00100000;
sleep_enable();
set_sleep_mode(SLEEP_MODE_STANDBY);
EIMSK |= _BV(INT0); //enable INT0
ADCSRA = 0; //disable ADC
cli();
mcucr1 = MCUCR | _BV(BODS) | _BV(BODSE); //turn off the brown-out detector
mcucr2 = mcucr1 & ~_BV(BODSE);
MCUCR = mcucr1;
MCUCR = mcucr2;
sei(); //ensure interrupts enabled so we can wake up again
sleep_cpu(); //go to sleep
sleep_disable(); //wake up here
Serial.print("Wake\n");
}
Aparentemente, Timer1 no puede salir de la CPU del modo de suspensión. ¿Tienes alguna idea de cómo manejar eso, o dónde está el problema?
Los modos de suspensión más profundos apagan más del chip para reducir el consumo de corriente.
Los modos más profundos apagan los contadores del temporizador ya que consumen bastante corriente.
Use el temporizador de vigilancia en modo de interrupción en su lugar, o para un apagado máximo, use un decaimiento RC externo controlado por micro para interrumpir el micro después de un período corto.
Sleep_Mode_Standby no permite que Timer1 se ejecute ya que el dominio del reloj está apagado. Solo puede usar Timer1 en Sleep_Mode_Idle, su código debería funcionar si usa ese modo.
Use el WDT ya que tiene su propio reloj a 128 kHz que se activa cuando está encendido.
Un código como este puede funcionar para usted:
#include <avr/sleep.h>
#include <avr/wdt.h>
void setup () { }
const byte LED = 13; //Nano LED
void flashLED ()
{
pinMode (LED, OUTPUT);
// Flash the LED
for (byte i = 0; i < 10; i++)
{
digitalWrite (LED, HIGH);
delay (50);
digitalWrite (LED, LOW);
delay (50);
}
}
// Watchdog ISR routine
ISR (WDT_vect)
{
wdt_disable(); // disable watchdog
// Return from ISR
}
void loop ()
{
flashLED ();
// disable ADC
ADCSRA = 0;
// clear all MCUSR flags
MCUSR = 0;
// allow changes, disable reset pin
WDTCSR = bit (WDCE) | bit (WDE);
// set interrupt mode and an interval
WDTCSR = bit (WDIE) | bit (WDP2) | bit (WDP1) | bit (WDP0); // set WDIE, and 2 second WDT delay
wdt_reset(); // Start the timer
set_sleep_mode (SLEEP_MODE_PWR_DOWN);
noInterrupts (); // timed sequence follows
sleep_enable();
// turn off brown-out detector
MCUCR = bit (BODS) | bit (BODSE);
MCUCR = bit (BODS);
interrupts ();
sleep_cpu ();
// Sleep here in powerdown state
// Return here after WDT return from ISR
// disable sleep
sleep_disable();
// Do your thing here
}
jimmyb