problem with PowerDown and INT0 ISR

Go To Last Post
8 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I hope there is no issue with posting codes that are running on Arduino architecture (Setup,Loop rather than Main,While(1)).

The issue that I have is in INT0 service in which it doesn't perform properly when MCU is set in PowerDown mode. I declared a global variable as "counter" and put it inside INT0 ISR but its value doesn't change.

To make sure that the issue is coming from Sleeping of MCU, I commented "sleepNow();" inside Loop(), line 178, and un-commented the following 3 lines and everything was working as expected and variable "counter" was changing correctly. I think I haven't constructed sleeping part properly and that causes this issue. I tried to play with the code and searched Google, but I couldn't find that much help. I also looked at these topics, topic1 and topic2, but those didn't help me too.

 

Simplified code is here:

 

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

#define Voltage A7                 // Analog Pin A7 | ADC7 - QFN pin 22
#define En_V A0                  // Analog Pin A0 - PC0 -QFN pin 23 | Trigger pin to turn on MOSFETs
#define En_Sensors 9             // Digital Pin D9 - PB1 -QFN pin 13 | Trigger pin to turn on sensor
#define En_I2C2 3
#define En_Sigfox A1       // GPIO9 On Sigfox - It is Set to PULL UP by default in Sigfox module. Set to LOW for waking up

#define SF_rxPin 5     //Sigfox
#define SF_txPin 6     //Sigfox

boolean debug =  true;
SoftwareSerial Sigfox (SF_rxPin, SF_txPin);

///////////////////////Interrupt changes////
const int interruptPin = 2; // INT0 - PD2 | INT1 - PD3
const uint8_t LED = A2;
volatile uint8_t counter = 0;
volatile bool IntFlag = false;
volatile bool WdtFlag = false;
////////////////////////////////////////////
uint8_t MCU_Sleep_Cycle = 4;

void wakeUp()
{
  static unsigned long last_millis = 0;
  unsigned long m = millis();

  if (m - last_millis < 150)
  {
    // ignore interrupt: probably a bounce problem
  }
  else
  {
    sleep_disable();
    detachInterrupt(0);// INT0 - PD2 | INT1 - PD3
    counter++;
    IntFlag = true;
  }
  last_millis = m;
}

ISR (WDT_vect)
{
  // WDIE & WDIF is cleared in hardware upon entering this ISR
  wdt_disable();
  WdtFlag = true;
}

void Sleep_Sigfox() {
  digitalWrite(En_Sigfox, HIGH);     // to go into DeepSleep mode
  setPowerMode(2);
}

void setup() {
  pinMode(Voltage, INPUT);
  pinMode(En_V, OUTPUT);
  pinMode(En_Sensors, OUTPUT);
  pinMode(En_I2C2, OUTPUT);
  pinMode(SF_rxPin, INPUT);
  pinMode(SF_txPin, OUTPUT);
  pinMode(En_Sigfox, OUTPUT);

  pinMode(interruptPin, INPUT_PULLUP);

  if (debug) {
    Serial.begin(9600);
  }
  Sigfox.begin(9600);
  delay(3 * 500);
  analogReference(EXTERNAL);
  digitalWrite(En_I2C2, LOW);
  if (debug) {
    Serial.println(F("Hello"));
  }

  digitalWrite(En_Sensors, LOW);
  Sleep_Sigfox();
  delay(2 * 1000);

  attachInterrupt(0, wakeUp, LOW);
}

uint8_t volt() {
  uint16_t tmp = 0;
  float v1, tmp2, tmp3;
  uint8_t v;
  delay(2 * 100);
  tmp = analogRead(Voltage); //A7
  if (tmp > 0) {
    v1 = tmp * 3.3 / 1023;
    tmp2 = v1 * 840 / 33; //10*(51+33)/33;  to represent in 10*V, (51+33)/33 is a HW Voltage divider.
    tmp3 = round(tmp2);
    v = tmp3;
  }
  else {
    v = 0;
  }
  return (v);
}

void loop() {
  //delay(2 * 500);

  if (WdtFlag) {
    wdt_reset();

    if (debug) {
      Serial.println(F("\nI woke up"));
      //Serial.print("IntFlag: ");
      //Serial.println(IntFlag);
      Serial.print(F("WdtFlag: "));
      Serial.println(WdtFlag);
      Serial.print(F("counter: "));
      Serial.println(counter);
      delay(500);
    }

    uint8_t rs = 0;
    uint8_t v = 0;
    char to_be_sent[12];
    memset(to_be_sent, 0, sizeof(to_be_sent));
    digitalWrite(En_V, HIGH);

    delay(300);

    v = volt();
    rs = counter;
    sprintf(to_be_sent,  "%2X%02X", v, rs);
    digitalWrite(En_V, LOW);

    if (debug) {
      Serial.print(F("Volt*10: "));
      Serial.println(v);
      Serial.print(F("RS-count: "));
      Serial.print(rs);
      Serial.println(F("\n"));
      delay(1000);
    }

    ///////////////////////////////// Send Values here
    delay(1 * 50);
    if (debug) {
      Serial.print(F("Sending Message: "));
      Serial.println(to_be_sent);
      delay(20);
    }
    counter = 0;
  }

  if (debug) {

    Serial.print(F("MCU is going into DeepSleep for "));
    Serial.print(MCU_Sleep_Cycle);
    Serial.println(F(" cycles."));
    delay(40);
  }
  for (uint8_t i = 0; i < MCU_Sleep_Cycle ; i++) { //15 cycles = 2min | 6 cycles = 48sec
    if (debug) {
      Serial.print(F("."));
      delay(10);
    }
    sleepNow();
    //WdtFlag=true;
    //attachInterrupt(0, wakeUp, LOW);
    //delay(2*1000);
  }

}

String setPowerMode(uint8_t mode) {
  //0: Software reset
  //1: sleep, send a break to wake up
  //2: deep_sleep, toggle GPIO0 or Reset_N to wake up

  if (debug) {
    Serial.print("Sigfox Power mode: " + String(mode) + "\t");
    //Serial.print(res);
    switch (mode) {
      case 0: Serial.println(F("Soft reset is done.\n")); break;
      case 1: Serial.println(F("Module is in sleep mode. zZzZ\n")); break;
      case 2: Serial.println(F("Module is in DeepSleep mode. zZzZ\n")); break;
    }
    delay(500);
  }

  Sigfox.print("AT$P=");
  Sigfox.print(mode);
  Sigfox.print("\r");
  //String res = getData();
  //return res;
}

void sleepNow() {
  //cli();   //disable interrupts
  EIFR |= (1 << INTF0); // Clearing INTF0 by setting this bit up. Int0 flag cleared.
  EIMSK |= (1 << INT0); //enable INT0

  byte adcsra_save = ADCSRA; //ADC backup
  byte WDTCSR_save = WDTCSR; //WDT backup

  WdtFlag = false;
  IntFlag = false;

  sleep_enable();          // enables the sleep bit in the mcucr register. so sleep is possible. just a safety pin
  set_sleep_mode(SLEEP_MODE_PWR_DOWN);   // sleep mode is set here
  ADCSRA &= ~(1 << 7); //clear bit 7 of register ADCSRA and disable ADC
  sleep_bod_disable();
  sei();//enable interrupts
  attachInterrupt(0, wakeUp, LOW); //when pin 2 gets LOW, run function wakeUp() // INT0 - PD2 | INT1 - PD3
  wdt_enable(WDTO_8S);
  WDTCSR |= (1 << WDIE);
  //sleep_mode();// here the device is actually put to sleep. THE PROGRAM CONTINUES FROM HERE AFTER WAKING UP
  sleep_cpu ();

  sleep_disable();
  sei();
  ADCSRA |= (1 << 7); //Set bit 7 and Enable ADC
  ADCSRA = adcsra_save;  // Config ADC again as before
}

 

What MCU is used?

ATMEGA328P running on 3.3V - 8MHz

 

What I want to do?

I would like to put MCU in deep sleep mode for a fixed period* (e.g. 15 minutes, though in the code above I have reduced time to less than a minute (6*8sec)) , but monitor number of times the external interrupt is happening. At then end of 15 minutes when MCU is waking up I would like to get total number of variable "counter", which was increased by INT0 ISR. 

 

What is the issue? 

The issue is that variable "counter" is not increasing when MCU is in sleep mode. MCU sleeps and consumption goes down. WDT is also working. INT0 wakes up the MCU, but value of counter is not changing.

 

What did I do?

I tried to run the code while MCU is not sleeping and variable "counter" was working fine. So, I suspect my sleeping instructions is not correct. To do this, I commented line 178, sleepNow();, and un-commented lines 179-181 at the end of main void Loop(){}. Please see attached picture.

 

What do I expect?

I expect that while MCU is in sleep mode, it can count the number of external interrupts.

 

What IDE, compiler and platform am I using?

Arduino IDE adjusted for board Arduino pro mini 3.3V - Windows 8 64bit

But the board that I have is not Arduino IDE. I just use Arduino IDE adjusted for Arduino pro mini since it has the same settings and compilation is easier for me.

 

Thanks

Nima

Attachment(s): 

Last Edited: Thu. Jan 30, 2020 - 08:30 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Nimasaj wrote:
I hope there is no issue with posting codes that are running on Arduino architecture

There's a forum specifically for that:

 

https://www.avrfreaks.net/forums/arduino

 

 

Top Tips:

  1. How to properly post source code - see: https://www.avrfreaks.net/comment... - also how to properly include images/pictures
  2. "Garbage" characters on a serial terminal are (almost?) invariably due to wrong baud rate - see: https://learn.sparkfun.com/tutorials/serial-communication
  3. Wrong baud rate is usually due to not running at the speed you thought; check by blinking a LED to see if you get the speed you expected
  4. Difference between a crystal, and a crystal oscillatorhttps://www.avrfreaks.net/comment...
  5. When your question is resolved, mark the solution: https://www.avrfreaks.net/comment...
  6. Beginner's "Getting Started" tips: https://www.avrfreaks.net/comment...
Last Edited: Tue. Jan 28, 2020 - 03:22 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Your code for wakeup seems to be using millis() to calculate some time delta but the millis() timer interrupt will not run while the clock is stopped. You could take a millis() reading, then immediately sleep for 3 days and when you wake up the next millis() reading might only be a few microseconds on from the previous reading.

 

As such:

  if (m - last_millis < 150)

may always be true and hence always:

    // ignore interrupt: probably a bounce problem

Presumably you have some system design notes where you have thought through all the possible scenarios and have discounted such  thing?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0
static unsigned long last_millis = 0;
  unsigned long m = millis();

  if (m - last_millis < 150)

 

Additionally, even if the timer running millis() did run when the MCU was asleep (which it doesn't), last_millis is a locally declared variable that is always 0 at point of comparison here.

 

m is likely well beyond 150 by the time the first interrupt runs. last_millis needs to be a global variable. But it still won't work because the timer counting millis isn't running.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0


MalphasWats wrote:
last_millis is a locally declared variable that is always 0 at point of comparison here.
Err no. It is "static" (so it is held in .bss not on the stack and IS there holding the last value at the next invocation).

 

For example:

#include <windows.h>
#include <stdio.h>

int count() {
    static int count = 0;
    return count++;
}

int main(void) {
    printf("count= %d\n", count());;
    printf("count= %d\n", count());;
    printf("count= %d\n", count());;
    printf("count= %d\n", count());;
}

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I'm sure this is a dumb question, but why does the mpu need to sleep? 

Is this a battery powered project so your attempting to save power?

Otherwise sleep may not be needed, or at most, only idle mode sleep (which keeps the timers running) may be your best option.

Jim

 

Click Link: Get Free Stock: Retire early! PM for strategy

share.robinhood.com/jamesc3274
get $5 free gold/silver https://www.onegold.com/join/713...

 

 

 

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

clawson wrote:
Err no. It is "static" (so it is held in .bss not on the stack and IS there holding the last value at the next invocation).

Oops, sorry, you're right of course. I'm not sure why I didn't notice the static.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

awneil ,

Thanks for moving this topic to the right section.

 

@ ki0bk ,

That's right. This is a battery-powered unit. So current draw is important.

 

clawson ,

Thanks for the hint. I changed INT0 ISR to the following one and implemented bouncing protection to the HW side using a RC filter.

 

void wakeUp()
{
    detachInterrupt(0);// INT0 - PD2 | INT1 - PD3
    counter++;
    IntFlag = true;
}

But still, the counter variable is not changing its value. It seems every time external interrupt happens the board resets and runs from scratch, from Setup(). Again, this is only happening when the board is in DeepSleep mode and everything works fine while the board is not in DeepSleep mode.

 

I tried to move the incremental of counter variable to the beginning of main Loop() like the following, but still reads zero.

void loop() {
  delay(2 * 500);
  if (IntFlag){ // here
    counter++;  // here
    }           // here

  if (WdtFlag) {
    wdt_reset();
    .
    .
    .

 

All your suggestions are welcome.