Is there a way to read the WDT timer/counter value?

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

I apologize if this was asked before. This is the problem (for atmega 1284P):

 

1. I enable the watchdog (8 seconds)

2. I put the device (atmega1284P) to sleep in power-down or power-save mode.

3. A PCINT wakes it up before the 8 second timeout.

4. I need to know how log it slept.

 

I'd really like to avoid using the Timer2, since it can only count a maximum of 256 * 1024 MCU cycles (it has an 8 bit resolution and a maximum pre-scale factor of 1024).

 

The code looks something like this:

 

wdt_enable(WDTO_8S);

sleep_cpu();

...

ISR(WDT_vect)
{

  // Here's where I need to know how long the device slept. I need milliseconds, or MCU cycles, or Watchdog oscillator ticks, or whatever else.

}

 

Thanks a lot!

 

Bogdan.

This topic has a solution.
Last Edited: Thu. Oct 12, 2017 - 05:39 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I think there is no way to read WDT counter value, but I think you can use Timer2 to measure long periods with high resolution. My solution is:

- set Timer2 prescaler to the value that gives the resolution you require

- create the variable for example volatile uint16_t timer2_overflows_count

- create Timer2 overflow interrupt handler that increases timer2_overflows_count

- you can easily calculate the elapsed time using value of timer2_overflows_count and current value of Timer2 counter

You need also modify sleep_cpu() function so the MCU is put again to sleep state after the wakeup by Timer2 overflow.

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

MarekJ71​, thank you so much for your solution.

 

I have thought of this myself, but there still are a few unanswered questions for me:

 

1. I have to call sleep_cpu() again at the end of my Timer2 interrupt handler, right? But do I also have to call sei() before calling sleep_cpu()? Probably yes.

 

2. What if a PCINT interrupt is triggered right before the Timer2 overflow (or at the same time), which one will be called first? I mean, isn't there a chance that PCINT handler will get called after the timer overflow (TCNT2 = 0), but before the overflow interrupt was called? Probably not, but I'm just asking.

 

3. Isn't there a risk of a stack overflow? I mean, if Timer2 overflow interrupts keep coming, the ISR(TIMER2_OVF_vect) will never return, but it will get recursively called over and over again...

 

4. Most important thing: What if a PCINT or WDT timeout interrupt is triggered while I handle the Timer2 overflow? Interrupts are disabled, so a flag will be set, but no code will be called. But what happens after I call sei() or sleep_cpu() from inside my Timer2 overflow handler? Will the MCU be awaken immediatley, as it should?

 

This is what I mean:

 

ISR(TIMER2_OVF_vect)
{
  overflowCounter++;

  sei(); // Is this needed? Probably yes...

  sleep_cpu(); // What if a PCINT or WDT interrupt was triggered immediately before the call to sleep_cpu()? Probably I can check a flag, but what if the interrupt is triggered after I check that flag? Won't this result in an unnecessary call to sleep_cpu()?
}

 

Thank you for your time!

 

Bogdan.

Last Edited: Thu. Oct 12, 2017 - 02:37 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

BogdanF77 wrote:
Isn't there a risk of a stack overflow? I mean, if Timer2 overflow interrupts keep coming, the ISR(TIMER2_OVF_vect) will never return, but it will get recursively called over and over again...

No.

It wont recursively call itself. (Recursion is just that, a function calling itself.)

 

It will be called repeatedly, like this: Timer overflows, ISR is called (and thus interrupts are disabled), ISR ends (and interrupts a re-enabled. If, at this point, the timer has overflowed again then the ISR will be called again - but the first call has exited.v Thus No stack overflow because of "recursion" because "recursion" is not at play.

 

Your next question might be: What happens if timer overflows faster than the ISR can handle? The answer is: You lose overflows. Like this: Timer overflows (i), ISR gets called ans starts executing, timer overflows (ii), timer overflows(iii), ISR finishes, ISR get executed again because an overflow is again flagged. There's three overflows, but only two executions of the ISR.

 

To avoid this, keep your ISR as short ass they can be. You know how often your timer overflows, so you know (at least roughly) how long your ISR can be allowed to execute. Of-course you need to keep well below that if anything else is to have a chance to execute (i.e. your programs "main loop").

Happy 75th anniversary to one of the best movies ever made! Rick Blane [Bogart]: "Of all the gin joints, in all the towns, in all the world, she walks into mine."

 

"Some questions have no answers."[C Baird] "There comes a point where the spoon-feeding has to stop and the independent thinking has to start." [C Lawson] "There are always ways to disagree, without being disagreeable."[E Weddington] "Words represent concepts. Use the wrong words, communicate the wrong concept." [J Morin] "Persistence only goes so far if you set yourself up for failure." [Kartman]

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

JohanEkdahl wrote:
Timer overflows, ISR is called (and thus interrupts are disabled), ISR ends (and interrupts a re-enabled

 

That's the problem, the ISR does not end. The ISR calls sei() and then sleep_cpu() at the end, and while waiting for sleep_cpu() to return (could take as long as 8 seconds), Timer2 might overflow again. Or maybe I'm missing something...

This reply has been marked as the solution. 
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 1

1. I have to call sleep_cpu() again at the end of my Timer2 interrupt handler, right? But do I also have to call sei() before calling sleep_cpu()? Probably yes.

No. I do it in the way like the following, assuming, that TIMER1 overflow is used for periodic wakeup, for example every 10ms:

volatile uint8_t flag_timed_wakeup;

ISR(TIMER1_OVF_vect) // I can't remember correct name, but this is only an example
{
    flag_timed_wakeup = 1;
}

int main(void)
{
    //.
    //.
    //initialization code
    //.
    //.
    for (;;)
    {
        for (;;)
        {
            sleep_cpu ();
            if (flag_timed_wakeup)
            {
                flag_timed_wakeup = 0;
                break;
            }
        }
        //.
        //.
        //do tasks
        //.
        //.
    }
}

This way all periodic tasks are executed with correct period, for example every 10ms and I also can use other interrupts. And never call sei() in interrupt handler unless you really know what you are doing.

2. What if a PCINT interrupt is triggered right before the Timer2 overflow (or at the same time), which one will be called first? I mean, isn't there a chance that PCINT handler will get called after the timer overflow (TCNT2 = 0), but before the overflow interrupt was called? Probably not, but I'm just asking.

3. Isn't there a risk of a stack overflow? I mean, if Timer2 overflow interrupts keep coming, the ISR(TIMER2_OVF_vect) will never return, but it will get recursively called over and over again...

There is no overflow risk if you don't enable interrupts in interrupt handler.

4. Most important thing: What if a PCINT or WDT timeout interrupt is triggered while I handle the Timer2 overflow? Interrupts are disabled, so a flag will be set, but no code will be called. But what happens after I call sei() or sleep_cpu() from inside my Timer2 overflow handler? Will the MCU be awaken immediatley, as it should?

With the code like my example above, the waiting interrupt will be handled immediately after return from the pending interrupt handler and I think everything will work as expected.

 

In this procedure:

ISR(TIMER2_OVF_vect)
{
  overflowCounter++;
  sei(); // Is this needed? Probably yes...
  sleep_cpu(); // What if a PCINT or WDT interrupt was triggered immediately before the call to sleep_cpu()? Probably I can check a flag, but what if the interrupt is triggered after I check that flag? Won't this result in an unnecessary call to sleep_cpu()?
}

you should leave only overflowCounter++.

Last Edited: Thu. Oct 12, 2017 - 04:04 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Don't do anything, except possibly set a flag, in the ISR.

Put the good stuff after sleep_cpu().

International Theophysical Year seems to have been forgotten..
Anyone remember the song Jukebox Band?

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

BogdanF77 wrote:
The ISR calls sei() and then sleep_cpu() at the end,

"Doctor, doctor, it hurts when I do THIS!"

"Then don't do THAT anymore.  That will be $82."

 

1)  In a real app with multiple wakeup sources, you don't want to try to unwind depending on wakeup source.

2)  As you opined, you are forcing re-entrancy with all of its attendant problems.

 

Short answer:  Never go to sleep inside an ISR on an AVR8.  Go to sleep properly in the mainline.

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

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

Thanks everybody for your comments.

 

MarekJ71​, your example might work, with some additions (setting a flag in WDT and PCINT interrupts, too, so I'll know why the device was awaken). Thanks a lot!

 

Bogdan.