Restoring an interrupt after Snooze

ChrisRowland

Active member
Hi,

I'm trying to write a morse keyer using a Teensy LC.
I'm using Snooze.sleep to reduce the current consumption while waiting for a key to be pressed.
The key press and release events are detected in ISRs.
This is all fine using a spin loop to wait for a key change event but if I use Snooze.sleep it works most of the time but not always. Sometimes it recovers but eventually it gets into a state where neither of the wake up events are seen.

This is the code. cut down to a minimum
Code:
// snooze with interrupt test
#define USE_SNOOZE

#ifdef USE_SNOOZE
#include <Snooze.h>
#endif

#define DIT_PIN 8
#define TIP_SW_PIN 9
#define COM_PIN 10
#define RING_SW_PIN 10
#define DAH_PIN 12

#define LED_PIN 13
#define TONE_PIN 17

#ifdef USE_SNOOZE
//load snooze drivers
SnoozeDigital digital;
Snoozelc5vBuffer lc5vBuffer;

// and the snooze block, with the drivers?
SnoozeBlock config_teensyLC(lc5vBuffer, digital);
#endif

// used to detect the dit and dah switches
volatile bool ditDown, dahDown, change;
int dotLen = 60;

void setup()
{
  
  pinMode(DIT_PIN, INPUT_PULLUP);
  pinMode(DAH_PIN, INPUT_PULLUP);
  pinMode(COM_PIN, INPUT);      // connected to ground
  pinMode(TIP_SW_PIN, INPUT);   // unused
  pinMode(RING_SW_PIN, INPUT);   // unused
  
  pinMode(LED_PIN, OUTPUT);
  pinMode(TONE_PIN, OUTPUT);
  
  digitalWrite(LED_PIN, HIGH);
  sender(1);

  ditDown = dahDown = change = false;

  // set up normal interrupts
  attachInterrupt(digitalPinToInterrupt(DIT_PIN), ditEvent, CHANGE);
  attachInterrupt(digitalPinToInterrupt(DAH_PIN), dahEvent, CHANGE);

  sender(2);
#ifdef USE_SNOOZE  
  // set up snooze interrupt/wakeup input pins
  digital.pinMode(DIT_PIN, INPUT_PULLUP, CHANGE);
  digital.pinMode(DAH_PIN, INPUT_PULLUP, CHANGE);
#endif
  sender(3);
}

void loop()
{
  int who = 0;
  // go to sleep, wake on a pin change
  digitalWrite(LED_PIN, LOW);
  
#ifdef USE_SNOOZE
  who = Snooze.sleep(config_teensyLC );  // return module that woke processor
  
  if (who == DIT_PIN)
  {
    noInterrupts();
    ditDown = !digitalRead(DIT_PIN);
    interrupts();
  }
  if (who == DAH_PIN)
  {
    noInterrupts();
    dahDown = !digitalRead(DAH_PIN);
    interrupts();
  }
#else
  change = false;
  while(!change);
#endif

  digitalWrite(LED_PIN, HIGH);

  // we stay in this loop while a key is on
  while (ditDown || dahDown)
  {
    if (ditDown)
    {
      sender(1);    //MorseSymbol.Dit);
    }
    if (dahDown)
    {
      sender(3);    //(MorseSymbol.Dah);
    }
  }  
}

void sender(int dots)
{
  tone(TONE_PIN, 800);
  delay(dotLen * dots);
  noTone(TONE_PIN);
  delay(dotLen);
}

// paddle Pine interrupts
void dahEvent()
{
  dahDown = !digitalRead(DAH_PIN);
  change = true;
}

void ditEvent()
{
  ditDown = !digitalRead(DIT_PIN);
  change = true;
}

Does anyone have any suggestions for finding out what's going wrong. I've tried using AttachInterrupt after the exit from Snooze and it didn't seem to help.

I know this trivial example doesn't need the key events in an ISR but the full code does.

Thanks,

Chris
 
A few more thoughts, prompted by reading the "Power Management for Kinetis
MCUs" AN.

I'd assumed that the sleep process took over the pin interrupts specified but I'm not sure this is the case. The power management system seems to be separate from the interrupts. Some exit modes talk about resuming the interrupt. If that's the case then my attempt to do the ISR's job, and particularly disabling interrupts may be doing more harm than good.

It isn't obvious what state the various Snooze functions the processor is put into. One thing I've found is that if I select deepSleep instead of sleep the input pins, which have a pinMode of INPUT_PULLUP go low and than means they can't change. Would an external pullup allow this mode to be used?

I'm finding that although the current is reduced, from about 50mA to about 10mA this isn't nearly as low as I'd expect. Are there other things that canbe done, for example the AN says "Unused pins should be configured in the disabled state, mux(0), to prevent unwanted leakage (potentially caused by floating inputs)."
Is there a way to disable the unused pins (e.g. pinMode(pin, DISABLED);?

I could easily be over thinking this, it could be a lot simpler than I think.

Chris
 
I've solved this, at least well enough for now.

The trick was not to use Snooze but to use chipaudette's idea of calling WFI.

This gets me down to 18mA of which 10mA is the quiescent current for the voltage regulator.

Removed the regulator and run it on 3 AAs, 7.8mA is OK. Less would be nice but it needs to be down to a few microA to not need a switch.

The wait loop now looks like this:
Code:
  while (!change)
    asm(" WFI");  // this reduces power by about 60%, it waits for one of my interrupts.
  change = false;

change is set in my ISRs.

This also has the advantage that Serial continues to work and I don't need to press the button to get into program mode.

Would disabling the peripherals (USB, ADC, EEPROM, Touch, Timers, etc., help?) Or because they aren't used they aren't enabled.
 
Back
Top