Forgetful 328P during sleep?

Hi all,

I'm working on my 18650 charger still and all is almost done but then I noticed that whenever I sleep in any mode the unit restarts like its been reset clearing any counters. Now I know I could use the EEPROM but that has only 100,000 write cycles and using it every 8 seconds would burn it out hella quick. Is there any other way anyone knows of to just hold a value during sleep? Perhaps keep the SRAM powered up but the rest sleeps or something. My intended application needs to count how many times it did an 8 second sleep so that after 12 hours it can start to disconnect the solar cells periodically from the battery to check the cells float voltage is above the battery charge voltage. I also figured worst case I could just not sleep and work in a reduced active mode at a lower clock with brownout off but really id like to save as much power as possible by using sleep mode power down in which I get nearly no draw.

Thanks

I don't believe RAM is cleared during sleep. Can you post your code?

Please forgive the messyness I've disabled lots just so I could isolate if it was adding the counter each wdt 8 second sleep. Thanks for taking a look.

//AceTK - Deepsleep powersaving single cell lion charger


#include <avr/sleep.h> // includes library for sleep
#include <avr/power.h> // includes library for sleep
#include <avr/wdt.h> // watchdog timer

unsigned short int SolVlts = 2;
volatile unsigned long AuxVolts = 0;
byte intCounter, adcsra, mcucr1, mcucr2;
volatile int f_wdt=1;
short unsigned int auxcharging = 0;
short unsigned int Relay1on = 9;
short unsigned int Relay1off = 10;
float AuxBatt = 0;
volatile short unsigned int val = 0; 
volatile unsigned long PowerCheckCount = 0;


ISR(WDT_vect)
{
  if(f_wdt == 0) 
  {
    f_wdt=1;
  }
  else
  {
    //Serial.println("WDT Overrun Error!!!");
  }
}

void enterSleep(void)
{
  
  //   *     SLEEP_MODE_IDLE         -the least power savings 
  //   *     SLEEP_MODE_ADC
  //   *     SLEEP_MODE_PWR_SAVE
  //   *     SLEEP_MODE_STANDBY
  //   *     SLEEP_MODE_PWR_DOWN     -the most power savings
  sleep_enable();
  set_sleep_mode(SLEEP_MODE_PWR_DOWN);
  // #ifdef WDTO_8S
  wdt_enable(WDTO_8S); // enable WDT 8 seconds
  wdt_reset();
  adcsra = ADCSRA;               //save the ADC Control and Status Register A
  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;
  /* Now enter sleep mode. */
  sleep_mode();
  
  /* The program will continue from here after the WDT timeout*/
  sleep_disable(); /* First thing to do is disable sleep. */
  ADCSRA = adcsra;  
  /* Re-enable the peripherals. */
  power_all_enable();
}

void SleepCheck()
{
int val;  
val = digitalRead(SolVlts);   // read the 1.092v/5v charge ready input pin
if (val > 0 && auxcharging == 0)                  // if 1.092v  present boolean equals 1 / true
{
  sleep_enable();
    wdt_reset();
    wdt_disable();
    set_sleep_mode(SLEEP_MODE_PWR_DOWN);
    EIMSK |= _BV(INT0);            //enable INT0
    adcsra = ADCSRA;               //save the ADC Control and Status Register A
    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
    ADCSRA = adcsra;               //restore ADCSRA
} 
}

ISR(INT0_vect)
{
    intCounter++;
    EIMSK &= ~_BV(INT0);           //one interrupt to wake up only
}

void AuxBattMan()
{
 AuxVolts = analogRead(A2); 
 AuxVolts = analogRead(A2); 
 AuxVolts = analogRead(A2);
 AuxVolts = analogRead(A2);  
 AuxBatt = AuxVolts / 240.70; // Aux cell resistive divide ratio converter 1023 / 240.70 = 4.25
 if (AuxBatt < 4.1 && auxcharging == 0)
{
 digitalWrite(Relay1on, HIGH);
  //delay(4);
  digitalWrite(Relay1on, LOW);
  auxcharging=1;
}

if (AuxBatt > 4.1 && auxcharging == 1)
{
  digitalWrite(Relay1off, HIGH);
  //delay(4);
  digitalWrite(Relay1off, LOW);
  auxcharging=0;
 }
}
 
void MiniSleep()
{
  if(f_wdt == 1)
  {
    f_wdt = 0;
    if (auxcharging == 1)
    {
    PowerCheckCount = PowerCheckCount+1;
    }
    // Re-enter sleep mode. //
    PowerCheckCount = PowerCheckCount+1;
    enterSleep();
  }
  else
  {
    // Do nothing. //
  }
}

void PowerChecker()
{
  if (PowerCheckCount >= 7) //12 hours = 4800 1 hour = 400 1 min = 7
  {
  digitalWrite(Relay1off, HIGH);
  delay(1);
  digitalWrite(Relay1off, LOW);
  PowerCheckCount = 0;
  }
 Serial.println(PowerCheckCount);
}

 
void StatusFlash()
{
  if (auxcharging == 0) 
  {
  digitalWrite(13, HIGH);
  delay(1);
  digitalWrite(13, LOW);
  delay(1);
  }
  if (auxcharging == 1)
  {
  digitalWrite(13, HIGH);
  delay(1);
  digitalWrite(13, LOW);
  delay(1);
  digitalWrite(13, HIGH);
  delay(1);
  digitalWrite(13, LOW);
  delay(1);
  }
}


void setup() {
 //CLKPR = (1<<CLKPCE);  
 //CLKPR = B00000011; // prescaler powersaver / rampup
// 0000 - divide crystal by 1 
// 0001 - divide crystal by 2
// 0010 - divide crystal by 4
// 0011 - divide crystal by 8
// 0100 - divide crystal by 16 
// 0101 - divide crystal by 32
// 0110 - divide crystal by 64
// 0111 - divide crystal by 128
// 1000 - divide crystal by 256  
 analogReference(INTERNAL); // sets reference to internal 1.092v
 pinMode(13, OUTPUT); // charge active light
 pinMode(SolVlts, INPUT);  // specifies pin 2 as available solar volts suitable for charge
 EICRA = 0x00;                 //configure INT0 to trigger on low level
 Serial.begin(9600);
 delay(2);
 if (PowerCheckCount > 0)
 {
 Serial.println(" Remembers Yay ");
 delay(3);
 }
 if (PowerCheckCount == 0)
 {
 Serial.println(" Starting Up Forgotten ");
 delay(3);
 }
 delay(4);
 /*** Setup the WDT ***/
 /* Clear the reset flag. */
 MCUSR &= ~(1<<WDRF);
 /* In order to change WDE or the prescaler, we need to
 * set WDCE (This will allow updates for 4 clock cycles).
 */
 WDTCSR |= (1<<WDCE) | (1<<WDE);
 /* set new watchdog timeout prescaler value */
 WDTCSR = 1<<WDP0 | 1<<WDP3; /* 8.0 seconds */
 /* Enable the WD interrupt (note no reset). */
 WDTCSR |= _BV(WDIE);
 //digitalWrite(Relay1on, HIGH);
 //delay(4);
 //digitalWrite(Relay1on, LOW);
 //delay(4);
 //digitalWrite(Relay1off, HIGH);
 //delay(4);
 //digitalWrite(Relay1off, LOW);
 auxcharging=0;
}




void loop()
{
 SleepCheck();  // divide#mosfet#pin2 12v solar ready triggers pin
 //SerialDiagnostics();
 //AuxBattMan(); 
 //Serial.print(AuxBatt);
 //delay(2);
 Serial.println("Working");
 delay(3);
 //Serial.print(AuxVolts);
 //delay(2);
 StatusFlash();
 delay(1000);
 MiniSleep(); // WDT Sleep for 8 seconds
 //PowerChecker();
}

Or if indeed anyone else has any ideas? Kind Thanks

I got a bit confused looking at your code. :slight_smile:

However I adapted my example from another page to prove that RAM is not cleared:

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

#define LED 13

// watchdog interrupt
ISR (WDT_vect) 
{
  wdt_disable();  // disable watchdog
}  // end of WDT_vect

unsigned int counter;

void myWatchdogEnable (const byte interval) 
{ 
  MCUSR = 0;                          // reset various flags
  WDTCSR |= 0b00011000;               // see docs, set WDCE, WDE
  WDTCSR =  0b01000000 | interval;    // set WDIE, and appropriate delay
  wdt_reset();
  
  byte adcsra_save = ADCSRA;
  byte prr_save = PRR;

  ADCSRA = 0;  // disable ADC
  PRR = 0xFF; // turn off various modules
  set_sleep_mode (SLEEP_MODE_PWR_DOWN);   // sleep mode is set here
  sleep_mode ();            // now goes to Sleep and waits for the interrupt
  
  ADCSRA = adcsra_save; // stop power reduction
  PRR = prr_save;
}  // end of myWatchdogEnable

void setup ()
{
}  // end of setup

void loop()
{
  Serial.begin (115200);
  Serial.println (counter++);
  Serial.flush ();
  
  while (!(UCSR0A & (1 << UDRE0)))  // Wait for empty transmit buffer
     UCSR0A |= 1 << TXC0;  // mark transmission not complete
  while (!(UCSR0A & (1 << TXC0)));   // Wait for the transmission to complete
  Serial.end ();

  // sleep bit patterns:
  //  1 second:  0b000110
  //  2 seconds: 0b000111
  //  4 seconds: 0b100000
  //  8 seconds: 0b100001

  // sleep for 8 seconds
  myWatchdogEnable (0b100001);  // 8 seconds
  
}  // end of loop

Serial output:

0
1
2
3
4
5
6
7
8
9
10
11
12
13
14
...

I can't see what is wrong with your code, but it will be something to do with all that fiddling around with registers. Take the simpler example, and expand on that.

Serial.println (counter++);
  Serial.flush ();
  
  while (!(UCSR0A & (1 << UDRE0)))  // Wait for empty transmit buffer
     UCSR0A |= 1 << TXC0;  // mark transmission not complete
  while (!(UCSR0A & (1 << TXC0)));   // Wait for the transmission to complete
  Serial.end ();

Why is this part needed please? Is it in place of delays to allow serial to work right? Also do I have to do it after every single serial println so that each line waits to complete? Then just serial end at end but why end serial if we are only sleeping? Also in your code I'm not sure if I see brownout detection disabled or not but I need that too. Do you have a simpler version than the one that uses all those registers I had before?

Why is this part needed please?

Serial data is output using interrupts. No interrupts during nap time. So, to ensure that all the data is output, you need to wait for the last character to be in the output byte. That's what Serial.flush() does. Then, you need to wait until that last byte has been shifted out.

Also do I have to do it after every single serial println so that each line waits to complete?

Create a function to do your printing. And waiting.

Thanks Nick and Paul I appreciate the help. I just need to integrate the second sleep mode now and find out a simple brownout disable command to replace the register version I was using in 1st example code. Also still a little curious what in my original code was causing the issue. But the code from nick does indeed work and I'm going to try to adapt and move over functions from my broken code a function at a time.

AceTK:
Also do I have to do it after every single serial println so that each line waits to complete?

Not at all. Just flush (and let that last byte come out) just before sleeping.

No interrupts during nap time.

Or possibly the interrupt would wake it. Depends on the sleep level. Probably not in power-down stage.