Project

General

Profile

loseSomeTime, interrupts and timing

Added by kingos over 5 years ago

I am attempting to do some work where I am merging reading temperatures on a cycle (60 seconds) with also detecting (and counting) a pulse (or button press) in a single device.

I effectively merged two emonTH examples, the DHT22 one and the Pulse one.

Unfortunately, what I found is that loseSomeTime was being interrupted by the interrupts for the button when they infrequently happen. Ok, fine, but there are multiple interrupts generated, so I put in the following kind of loop:

void dodelay(unsigned long ms)
{
  unsigned long expectedEndMillis = ms + millis();
  byte success = 1;
  do 
  {
    // Save Analog to Digital Converter registers - why? I know not!
    byte oldADCSRA=ADCSRA;
    byte oldADCSRB=ADCSRB;
    byte oldADMUX=ADMUX;

    success = Sleepy::loseSomeTime(ms); // JeeLabs power save function: enter low power mode for x seconds (valid range 16-65000 ms)    
    ADCSRA=oldADCSRA;         // restore ADC state
    ADCSRB=oldADCSRB;
    ADMUX=oldADMUX;
    if (!success)
    {
      unsigned long current = millis();
      ms = (expectedEndMillis > current) ? expectedEndMillis - current : 0;
      if (debug)
      {
        Serial.print("Not successful: ");Serial.print(current); Serial.print(" ms remaining: "); Serial.println(ms);
        Serial.flush();
      }
    }
  }
 while (!success && ms >= 16); 
}

This appeared to be having the right result at first, just track how much time has passed before interrupted and try again with the remainder, but the problem I am running into is the ‘guessing half the time’has passed’ problem in loseSomeTime. If I have a bunch of interrupts stacked up in a row, my ‘60’ seconds passes in about 2 seconds

So, I am looking at modifying loseSomeTime to more accurately track time, rather than guessing half which unfortunately is not a good guess for my code. Can I potentially use micros() instead for shorter periods? What are some other possible options? Is there something simple like just counting the number of iterations through the loop when it is interrupted and multiplying by 16ms? Any ideas for how to fix this problem?

Thanks so much for your help, I am quite new to Arduino platform so a bit lost as to how to solve this.

Thanks,
Andrew


Replies (9)

loseSomeTime, interrupts and timing - Added by jcw over 5 years ago

The problem is that AFAIK there is no way to read out the watchdog timer, so you can’t tell how much time was still left until the watchdog would have fired.

One way out is to replace one long loseSomeTime call with lots of small ones. The minimum sleep time is 16 ms, so you could do:

for (int i = 0; i < fullTime/16; ++i) Sleepy::loseSomeTime(16);

Then interrupts in between would have much less effect on the total time spent in sleep mode.

-jcw

> On 15 Nov 2014, at 13:59, redmine@jeelabs.net wrote:
>

RE: loseSomeTime, interrupts and timing - Added by JohnO over 5 years ago

I will watch this with interest. I had a need to count the number of seconds spent in power down and modified loseSomeTime to return the number of whole seconds it had been powered down. I guess you could increase the granularity to 16, 32, 64, 128, 256 or 512ms. This still has the problem that all bar one tick of the second has expired but not counted .
I think counting iterations or using micros will defeat the object of sleeping in power down mode.

RE: loseSomeTime, interrupts and timing - Added by jcw over 5 years ago

> I think counting iterations or using micros will defeat the object of sleeping in power down mode.

Agreed, the Arduino’s runtime µs and ms delays run at full power, i.e. consuming milliamps instead of microamps.

Cycling loseSomeTime() every 16 ms is different. Coming out of sleep mode and going back to sleep should take well under 100 µs.
So on a 16 ms cycle, that’s less than 1% of the time running at full power, the other 99% the µC is still kept in ultra low-power mode.

RE: loseSomeTime, interrupts and timing - Added by JohnO over 5 years ago

I added my stuff to Ports.cpp/h:

word Sleepy::pwrDownTimer () {
    unsigned int timeOff = 0;
    word millisAdjust;
    while (1) {
        millisAdjust = 2;         // If we are interrupted assume 2ms passed
        watchdogCounter = 0;
        watchdogInterrupts(6);    // 1024ms
        powerDown();
        watchdogInterrupts(-1);   // off

        // because there are lots of Serial.print interrupts around
        if (watchdogCounter != 0) {
            millisAdjust = 1024;  // Wasn't interrupted for 1024ms
        }
// Update millis as we go, if we are interrupted time(ms) should have moved on
// for the interrupting process        
#if defined(__AVR_ATtiny84__) || defined(__AVR_ATtiny85__) || defined (__AVR_ATtiny44__) || defined (__AVR_ATtiny45__)
        extern volatile unsigned long millis_timer_millis;
        millis_timer_millis += millisAdjust;
#else
        extern volatile unsigned long timer0_millis;
        timer0_millis += millisAdjust;
#endif
    if (millisAdjust == 2) break;
    ++timeOff;       
    }
    return timeOff;               // Complete seconds
}

My modded library is here: https://github.com/jcw/jeelib/tree/RF12Demo\_LowPower

RE: loseSomeTime, interrupts and timing - Added by JohnO over 5 years ago

Perhaps we could tweak it to supply the “6” value
watchdogInterrupts(6)
as a parameter from the calling program and just return the watchdog
counts for the main sketch to multiply up.

I wonder why your environment so so interrupt rich, is there any
point sleeping?

RE: loseSomeTime, interrupts and timing - Added by martynj over 5 years ago

Would it make life easier to de-bounce the switch in hardware to get a single interrupt per event?

RE: loseSomeTime, interrupts and timing - Added by kingos over 5 years ago

Ok, so JohnO’s solution is slightly more complicated version of jcw’s, but inside the library, correct?

Yes, this is all due to a bouncing switch, which I am debouncing in the ISR by keeping track of time.
The switch is going to trigger very irregularly (once every 5 minutes?) but I don’t want it to cause the timing for the collection of other information to vary.

I would love to debounce it in hardware, but I left my hardware skills behind many many years ago :(

Thanks for all your help

RE: loseSomeTime, interrupts and timing - Added by jcw over 5 years ago

Another option: If waking up every 16 ms as I suggested, the there’s no need for interrupts or debouncing - simply check the value of the i/o pin each time and you’re done.

Hardware debounce is also easy: a 0.1 uF cap to ground and a 10 k resistor to + is all you need, probably. Even the internal pull-up is probably fine, then you only need the capacitor.

In short: why spend brain cycles figuring out how to work around the watchdog timer deficiencies, when you can avoid the issue completely?

-jcw

> On 15 Nov 2014, at 16:39, redmine@jeelabs.net wrote:
>

    (1-9/9)