LowPower_Teensy3 delay function appears to hang

pictographer

Well-known member
In the example below, if delay() is changed to LP.delay(), I get a hang as described in the code comments below.

Code:
// LowPower_Teensy3 example demonstrating touch-to-wake functionality and 
// a bug in the delay(uint32_t msec, uint32_t cpu).
//
// Expected behavior: When power is first applied, the built-in LED blinks once. 
// When pin 0 is touched, the built-in LED blinks once follwed by a pause and
// blinks twice more.
//
// Buggy behavior: if LP.delay is used instead of delay, the power-on blink occurs, 
// however, the first time pin 0 is touched, only one bink occurs and the device becomes
// unresponsive.
//
// Tested with Mac OSX 10.9.4, Arduino 1.0.5, Teensyduino 1.20-rc1 using the 
// master branch of the LowPower_Teensy3 Library obtained July 31, 2014.
//
// Note: It is necessary to press the reset button to download new binaries 
// while running this example.
//
// John D. Corbett
// corbett@pictographer.com
// August 3, 2014


#include <LowPower_Teensy3.h>


TEENSY3_LP LP = TEENSY3_LP();
sleep_block_t LP_config;


//XXX If we use LP.delay instead of delay, the touchCallback does not get called.
void delayBug(int ms) {
  delay(ms);
  //LP.delay(ms, TWO_MHZ);
}


// Blink n times followed by an additional delay so consecutive calls can 
// be distinguished.
void blink(int n) {
  for (int i = 0; i < n; ++i) {
    digitalWrite(LED_BUILTIN, HIGH);
    delayBug(50);
    digitalWrite(LED_BUILTIN, LOW);
    delayBug(100);
  }
  delayBug(500);
}


// Blinks twice. Enters hibernation state.
void touchCallback() {
  blink(2);
  LP.Hibernate(&LP_config);  
}


// Blinks once. Configures hibernation mode to wake when pin 0 is touched. Enters
// hibernation state.
void setup()
{
  pinMode(13, OUTPUT);
  blink(1);
  // Configure waking on a touch event.
  LP_config.modules = TSI_WAKE;
  LP_config.tsi_pin = 0;
  // Calibrate touchRead by taking a reading. If the user is touching the pin when
  // this calibration is perfomed, the threshold may be too high to recognize
  // subsequent touch actions.
  LP_config.tsi_threshold = touchRead(LP_config.tsi_pin) + 4;
  LP_config.callback = touchCallback;
  
  // Enter hibernation until a touch event. Exit hibernation state via 
  LP.Hibernate(&LP_config);
}


void loop() {
  // Not reached.
  //
  // After power is applied, the following sequence occurs:
  //    setup:
  //      * Blinks once
  //      * Configures an interrupt for a touch event on pin 0.
  //      * Hibernates, suspending execution until a touch occurs on pin 0.
  //
  // After a touch interrupt is triggered, the following sequence occurs:
  //    setup:
  //      * Performs steps listed above.
  //    LP_config.callback:
  //      * Blinks twice
  //      * Hibernates as described above.
}

Paul and duff, thank you for making such wonderful stuff.

Cheers,
John
 
// Tested with Mac OSX 10.9.4, Arduino 1.0.5, Teensyduino 1.20-rc1 using the
// master branch of the LowPower_Teensy3 Library obtained July 31, 2014.

Also, discovered that LP.delay(ms, F_CPU) works ok.
 
i see you are using 2MHZ as the speed, are you using the the new speeds implemented in Teensyduino 1.19 and above from the arduino CPU speed drop down list? If so the library is not totally compatible if you go that route. I'm planning to update the library to be totally compatible with Teensyduino 1.19 and above. I think thats why using F_CPU works.

In the mean time there is a function LP.CPU(speed) that you can use to dynamically scale the cpu speed in your program. Then you can compile it at say 24MHz and then drop down to 2,4,8,16 MHZ when needed. Though depending on what functions you use that need the F_CPU it might not behave correctly. Thats why I ported over some things like Hardware Serial1,2,3 to play nice when changing the cpu speed dynamically.

If you just plan to run at 2MHZ from the Arduino drop down list then your way will work by using F_CPU. Or you could just use the regular delay. Basically the LP.delay(ms, speed) has some special power saving feature while waiting.
 
duff, thank you!

I was using 24MHz from the drop down menu and guessing (incorrectly) that LP.delay would change the CPU frequency as needed, though now that I think about it, it's not obvious why LP.delay should need to be told the CPU frequency.

At this point, I'm just getting started with the Low Power Teensy library. My main goal was to get wake on touch to work and it is working nicely. This will allow the applications I'm working on to spend most of their time hibernating.

For my Morse code gadget, I'm planning to implement an inactivity timeout and have a proof of concept working on my desk.

For my radio repeater voltage monitor, it only needs to wake up every 10 minutes to take a reading and make an announcement. This looks straightforward.

This would have taken me ages if I had to plow through all the registers, state machines, and bit twiddling. :)
 
I can see why that would confuse you so thanks for pointing that out. CPU speed parm is so the delay time will be as close to being correct as I could come up with along with some arm functions to reduce power.

If you see any problems with my touch sense waking let me know, that was one that I just looked at other source code and made some assumptions about sensitivity and such.
 
I promise to let you know of any problems I can document, especially regarding touch sense. :D

There's a little gotcha. Since we're forced to use the reset button when reprogramming, if the startup code uses touchRead() to get calibration data, it's easy to inadvertently touch or hold the Teensy near a touch pin and set the baseline "no touch" reading so high that touchRead doesn't generate an interrupt.

Regarding the delay CPU speed parm, I can see why the CPU speed is needed for the delay calculation, but I don't see why it isn't already known. There's a member for it in the TEENSY3_LP class. Wouldn't TEENSY3_LP::_cpu have the required value? Any changes to the CPU frequency should go through your API. Or if we're worried about other libraries that manipulate the CPU frequency, then maybe users of such libraries need a way to refresh (recreate) the TEENSY3_LP instance.
 
Back
Top