View previous topic :: View next topic |
Author |
Message |
peter-storm
Joined: 26 Jan 2008 Posts: 11
|
Long delay using watchdog timer |
Posted: Sat Jan 26, 2008 12:40 pm |
|
|
Hi all.
I have a PIC16f690 that must use the lowest possible power for as long periods of time (up to an hour). I would like to utilise the watchdog timer, which has a max time of 268 seconds, in the following way -
initially set timing variable = 6
device is asleep
WDT wake from sleep (268 sec)
decrement timing variable
is timing variable == 0?
if yes, perform_function()
else, sleep (for another 268 sec)
repeat.
Would this be possible? I would like to change the timing variable in "perform_function()" so that the duration my "pseudo-sleep" can be dynamically changed.
Cheers. |
|
|
Ttelmah Guest
|
|
Posted: Sun Jan 27, 2008 10:36 am |
|
|
Yes, totally possible, and a common solution.
Big caveat though, is that the times will _not_ be accurate. The WDT on your chip, runs from the LFINTOSC, which is an uncalibrated oscillator, and varies massively between chips, and with temperature/voltage. The range quoted in the data sheet, is 15KHz, to 45KHz, for the 'nominally' 31KHz oscillator. As such the '268' second selection, may vary to as little as 184 seconds, and up to 553 seconds.....
If you want accurate times, then I'm afraid you need to use timer1, and add an external crystal, or use an external RTC module.
Best Wishes |
|
|
peter-storm
Joined: 26 Jan 2008 Posts: 11
|
|
Posted: Sun Jan 27, 2008 4:51 pm |
|
|
Thanks, Ttelmah.
The precision isn't particularly important to me, so I have gone ahead and written the following:
Code: |
#include <16f690.h>
#fuses HS, WDT, NOPROTECT, BROWNOUT
#use delay(clock=8000000)
#use rs232(baud=19200,parity=N,xmit=PIN_B7,rcv=PIN_B5,bits=8)
#include <vl1001.c>
int timer = 5;
int delay = 500;
void main(){
setup_adc_ports(NO_ANALOGS|VSS_VDD);// This is because by default
//the AD inputs are analogs
setup_adc(ADC_OFF);// switch off the internal ADC module
setup_spi(SPI_SS_DISABLED);
setup_wdt(WDT_2304MS|WDT_TIMES_8);
setup_timer_0(RTCC_INTERNAL);
setup_timer_1(T1_DISABLED);
setup_timer_2(T2_DISABLED,0,1);
setup_comparator(NC_NC_NC_NC);
setup_vref(FALSE);
setup_oscillator(OSC_8MHZ|OSC_INTRC);
setup_uart(UART_WAKEUP_ON_RDA);
set_tris_c(0x00); // set portC as output
output_toggle(PIN_C0);
if (timer == 1){
output_toggle(PIN_C1);
timer = 5;
}
else
timer--
} |
However, C1 doesn't toggle every 5 toggles of C0, and I fear that the variable timer is being reset to 5 every time the PIC's WDT kicks in.
How would I make it not reset the value every time?
Cheers |
|
|
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
|
Posted: Sun Jan 27, 2008 5:03 pm |
|
|
In CCS, an 'int' is an unsigned 8-bit integer. Use 'long' or 'int16' instead.
Both are 16-bit unsigned integers in CCS. |
|
|
peter-storm
Joined: 26 Jan 2008 Posts: 11
|
|
Posted: Sun Jan 27, 2008 5:24 pm |
|
|
PCM programmer wrote: |
In CCS, an 'int' is an unsigned 8-bit integer. Use 'long' or 'int16' instead.
Both are 16-bit unsigned integers in CCS. |
Oh ok, thanks for the info. The delay variable isn't used in this example, but I'll use int16 for future code. |
|
|
ckielstra
Joined: 18 Mar 2004 Posts: 3680 Location: The Netherlands
|
|
Posted: Sun Jan 27, 2008 5:49 pm |
|
|
Code: | if (timer == 1){
output_toggle(PIN_C1);
timer = 5;
}
else
timer-- | Your code now loops 4 times. If you want to loop 5 times, than change to Code: | if (timer == 0){
... | An additional advantage of this change is shorter code. Comparing with 0 takes fewer instructions than comparing to any other value. |
|
|
peter-storm
Joined: 26 Jan 2008 Posts: 11
|
|
Posted: Sun Jan 27, 2008 6:02 pm |
|
|
ckielstra wrote: | Code: | if (timer == 1){
output_toggle(PIN_C1);
timer = 5;
}
else
timer-- | Your code now loops 4 times. If you want to loop 5 times, than change to Code: | if (timer == 0){
... | An additional advantage of this change is shorter code. Comparing with 0 takes fewer instructions than comparing to any other value. |
Thanks for the info, but the main issue I have is that PIN_C1 is not changing - the "if" statement is never satisfied, and I cannot work out why. |
|
|
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
|
Posted: Sun Jan 27, 2008 6:04 pm |
|
|
Quote: | How would I make it not reset the value every time? |
Don't initialize 'timer' above main(). Just declare it.
Example:
Initialize it at the start of main(). But, qualify the init with
a call to the restart_cause() function. If it's a normal power-on
reset, then set 'timer' to 5. If not, then don't touch it. Use an 'if'
statement to do this test. So when a WDT reset occurs, 'timer'
will not be set to 5.
See this file for an example of using restart_cause():
Quote: | c:\program files\picc\examples\ex_wdt.c |
|
|
|
peter-storm
Joined: 26 Jan 2008 Posts: 11
|
|
Posted: Sun Jan 27, 2008 6:10 pm |
|
|
PCM programmer wrote: | Quote: | How would I make it not reset the value every time? |
Don't initialize 'timer' above main(). Just declare it.
Example:
Initialize it at the start of main(). But, qualify the init with
a call to the restart_cause() function. If it's a normal power-on
reset, then set 'timer' to 5. If not, then don't touch it. Use an 'if'
statement to do this test. So when a WDT reset occurs, 'timer'
will not be set to 5.
See this file for an example of using restart_cause():
Quote: | c:\program files\picc\examples\ex_wdt.c |
|
Thank you very much, this has worked!
Thanks again to all that have contributed. |
|
|
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
|
Posted: Sun Jan 27, 2008 6:16 pm |
|
|
Actually, since you could have a Brownout reset, you should probably
change the test to look specifically for a WDT reset. Skip the init only
in that case. Do the init for all other types of reset. That would be
a more complete way to do it. (After thinking a little more about it). |
|
|
peter-storm
Joined: 26 Jan 2008 Posts: 11
|
|
Posted: Sun Jan 27, 2008 6:24 pm |
|
|
You have a good point, so I have replaced the NORMAL_POWER_UP: with default:, that should do the trick? |
|
|
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
|
Posted: Sun Jan 27, 2008 6:44 pm |
|
|
I think that will do it. Don't do anything in the WDT case. Just break.
In the default case, set timer to 5, then break. Only the WDT and default
cases should be in the switch statement. |
|
|
n-squared
Joined: 03 Oct 2006 Posts: 99
|
|
Posted: Sun Jan 27, 2008 11:34 pm |
|
|
Hi,
If I may add my two cents worth:
It is good practice to check for boundaries in real time systems.
That means that if the timer variable is not 1 to 5, you want to "bring it back home".
The way I implement such a timer is to start at zero and upcount and test for value 5 or greater:
Code: |
if (++timer >= 5)
{
timer = 0;
output_toggle(PIN_C1);
}
|
This method will prevent counting 100 or 200 cycles if noise caused the variable to go wild. |
|
|
|