Sleep and wake up using IO (solved)

Hi All,

I am a bit stuck here, I’m trying to do a little experiment with the SODAQ One sleep mode and wake up by using an IO.

I have a signal normally Low on D11, As soon as D11 has been low for 3 seconds the Sodaq must enter sleep mode. Then as soon as the D11 is set high it must wake up again.

I use a rising edge interrupt when the device is awake. I use the high interrupt when the device is asleep.

Still, the high interrupt seems to fire immediately regardless of the D11 state. What am I doing wrong?


static int sleep = 0;

void pirsensed ()
{
SetLedColor(ORANGE);
}

void setup()
{
pinMode(ENABLE_PIN_IO, OUTPUT);
digitalWrite(ENABLE_PIN_IO, HIGH);

SetLedColor(BLUE);

SerialUSB.begin(9600);
Serial1.begin(57600);
pinMode(11, INPUT);

delay(3000);

setupAwakeInterrupt();

}

void setupAwakeInterrupt()
{
detachInterrupt(digitalPinToInterrupt(11)); // TODO: Needed?
attachInterrupt(digitalPinToInterrupt(11), pirsensed, RISING);
}

void setupSleepInterrupt()
{
detachInterrupt(digitalPinToInterrupt(11)); // TODO: Needed?
SetLedColor(BLUE);
delay(1000);
attachInterrupt(digitalPinToInterrupt(11), pirsensed, HIGH);
}

void loop()
{
if (digitalRead(11) == HIGH) {
SetLedColor(GREEN);
sleep = 0;
}
else {
sleep++;
SetLedColor(RED);
delay(1000);

    if (sleep>= TIME_SLEEP) {
        systemSleep();
    }
}

}

void systemSleep()
{
setupSleepInterrupt();

SetLedColor(NONE);
delay(1000);

Serial1.println("sys sleep 30000");
delay(100);

pinMode(GPS_ENABLE, OUTPUT);
digitalWrite(GPS_ENABLE, LOW);

sodaq_wdt_disable();

SCB->SCR |= SCB_SCR_SLEEPDEEP_Msk;

__WFI();
sleep     = 0;
setupAwakeInterrupt();

}

A couple of notes:

On the SAMD boards you need to pass the pin number to the attach/detach interrupt routines, not the interrupt number. e.g. For D11 you would use something like:

attachInterrupt(11, pirsensed, RISING);

The RISING, FALLING and CHANGE modes do not normally work correctly in sleep mode. The reason for this is because those modes require a clock signal to detect. Ordinarily the source clock to the EIC is powered down. These modes can be enabled with some extra configuration of the specific GCLK in use. HIGH and LOW will work fine regardless.

Thanks. I tried using just 11 in stead of digitalPinToInterrupt. However, that does not change behaviour.

Is it even possible to re-attach an interrupt at all?

Thanks for thinking along with this.

Hi,

I managed to fix it. The problem lies in the attachInterrupt function. It is only able to set bits in the EIC->CONFIG[config].reg.
This causes both HIGH and RISING bits being set when re-attaching an interrupt. While I want to clear the high bit and set the rising bit when I reattach after wakeup.

I made the following code and this works correctly. It first clears all bits for the applicable pin in the EIC->CONFIG[config].reg and then just calls the regular attachInterrupt function.


/*

  • \brief Specifies a named Interrupt Service Routine (ISR) to call when an interrupt occurs.
  •    Replaces any previous function that was attached to the interrupt.
    

*/
void MyAttachInterrupt (uint32_t pin, voidFuncPtr callback, uint32_t mode)
{
static int enabled = 0;
uint32_t config;
uint32_t pos;

EExt_Interrupts in      = digitalPinToInterrupt(pin);

if ((in == NOT_AN_INTERRUPT) || (in == EXTERNAL_INT_NMI)) {
    return;
}

// Look for right CONFIG register to be addressed
if (in > EXTERNAL_INT_7) {
    config = 1;
}
else {
    config = 0;
}
pos                      = (in - (8 * config)) << 2;

/* CLEAR ALL PREVIOUSLY SET BITS */
EIC->CONFIG[config].reg &= ~((EIC_CONFIG_SENSE0_LOW_Val << pos) |
                             (EIC_CONFIG_SENSE0_HIGH_Val << pos) |
                             (EIC_CONFIG_SENSE0_BOTH_Val << pos) |
                             (EIC_CONFIG_SENSE0_FALL_Val << pos) |
                             (EIC_CONFIG_SENSE0_RISE_Val << pos));

attachInterrupt(pin, callback, mode);

}

void sleepirq()
{
SetLedColor(ORANGE);
}

void pirsensed()
{
SetLedColor(BLUE);
}

void setup()
{
pinMode(ENABLE_PIN_IO, OUTPUT);
digitalWrite(ENABLE_PIN_IO, HIGH);

SetLedColor(BLUE);

SerialUSB.begin(9600);
Serial1.begin(57600);
pinMode(11, INPUT);
pinMode(11, INPUT_PULLDOWN);

delay(3000);

setupAwakeInterrupt();

}

void setupAwakeInterrupt()
{
noInterrupts();
MyAttachInterrupt(11, pirsensed, RISING);
interrupts();
}

void setupSleepInterrupt()
{
noInterrupts();
MyAttachInterrupt(11, sleepirq, HIGH);
interrupts();
}

void loop()
{
if (digitalRead(11) == HIGH) {
SetLedColor(GREEN);
sleep = 0;
}
else {
sleep++;
SetLedColor(RED);
delay(1000);

    if (sleep>= TIME_SLEEP) {
        systemSleep();
        sleep = 0;
    }
}

}

void systemSleep()
{
Serial1.println(“sys sleep 30000”);
delay(100);

pinMode(GPS_ENABLE, OUTPUT);
digitalWrite(GPS_ENABLE, LOW);

sodaq_wdt_disable();

setupSleepInterrupt();

SetLedColor(NONE);

SCB->SCR |= SCB_SCR_SLEEPDEEP_Msk;

__WFI();

setupAwakeInterrupt();

}