You are on page 1of 24

© Gooligum Electronics 2014 www.gooligum.com.

au

Introduction to PIC Programming


Programming Enhanced Mid-Range PICs in C

by David Meiklejohn, Gooligum Electronics

Lesson 6: Interrupt-on-change, Sleep Mode and the Watchdog Timer

One of the most useful features of modern microcontrollers (including PICs) is their ability to enter a power-
saving “sleep” mode, where power drain may be less than a microwatt, facilitating the design of low-
powered devices without traditional on-off switches – the device can turn itself “off”. For example, the
Gooligum Christmas Star, based on a PIC12F683, runs on a pair of N-cell batteries, but will remain “shut
off”, with no significant battery drain, for a year or more, coming to life as soon as a pushbutton is pressed.
The latter feature relies on the PIC’s “interrupt-on-change” facility, which is often used to wake the device
from sleep. As we shall see, it can also (as the name suggests) be used to trigger an interrupt in response to a
changing input; similar to the external interrupt facility introduced in lesson 5.
Another facility usually found in modern microcontrollers (including PICs) is a “watchdog timer”, intended
to make a device more robust by providing a means of detecting situations where the program appears to be
hung, and then resetting the processor so that the system can recover.
But as we’ll see in this lesson, the watchdog timer can also be used to periodically wake the PIC from sleep,
a facility which makes it possible to design devices which spend most of their time sleeping, drawing very
little current (and hence power) on average.
In summary, this lesson covers:
 Interrupt-on-change
 Sleep mode (power down)
 Wake-up on change (power up)
 The watchdog timer
 Periodic wake from sleep
with examples implemented using XC8 (running in “Free mode”).

Interrupt-on-change
In lesson 5, we saw that the 12F1501’s external interrupt facility can used to trigger an interrupt on each
rising or falling transition on the INT (RA2) pin; useful when we need to respond to an external digital signal
more quickly than using a timer interrupt to poll the input every millisecond or so, without having to tie up
the processor with a tight polling loop. Instead, the processor can be going about other tasks, but still be able
to service the external event within microseconds of it occurring.
Note that the external interrupt is triggered on either a rising or falling edge (selectable by the INTEDG bit),
but not both. If rising edges are selected, falling edges will be ignored. This tends to simplify code, since we
are normally only interested in one type of transition.

Enhanced Mid-Range PIC C, Lesson 6: IOC, Sleep Mode and the Watchdog Timer Page 1
© Gooligum Electronics 2014 www.gooligum.com.au

The enhanced mid-range PIC architecture only supports a single external interrupt pin. However, the
interrupt-on-change facility can be used if you need to respond quickly to a number of digital signal sources.

Every pin in PORTA can be configured to detect either rising or falling edges (or both).
Rising edge detection for PORTA is enabled via the IOCAP register:

Bit 7 Bit 6 Bit 5 Bit 4 Bit 3 Bit 2 Bit 1 Bit 0

IOCAP - - IOCAP5 IOCAP4 IOCAP3 IOCAP2 IOCAP1 IOCAP0

If a bit in the IOCAP register is set, rising edges will be detected on the corresponding PORTA pin.
For example, to enable rising edge detection for RA2, we would set IOCAP2 = 1.

Similarly, falling edge detection for PORTA is enabled via the IOCAN register:
Bit 7 Bit 6 Bit 5 Bit 4 Bit 3 Bit 2 Bit 1 Bit 0

IOCAN - - IOCAN5 IOCAN4 IOCAN3 IOCAN2 IOCAN1 IOCAN0

If a bit in the IOCAN register is set, falling edges will be detected on the corresponding PORTA pin.
For example, to enable falling edge detection for RA3, we would set IOCAN3 = 1.

If, after enabling either type of edge detection for a pin in PORTA, an appropriate edge is detected, the
corresponding flag on the IOCAF register will be set1:
Bit 7 Bit 6 Bit 5 Bit 4 Bit 3 Bit 2 Bit 1 Bit 0

IOCAF - - IOCAF5 IOCAF4 IOCAF3 IOCAF2 IOCAF1 IOCAF0

For example, if IOCAP2 is set and a rising edge is detected on RA2, the IOCAF2 flag will be set.
If IOCAN2 is clear and a falling edge is detected on RA2, nothing will happen – IOCAF2 is not affected
because falling edge detection has not been enabled.
However if IOCAN3 is set and a falling edge is detected on RA3, the IOCAF3 flag will be set.
And finally, if IOCAN3 and IOCAP3 are both set, IOCAF3 will be set if any type of change (rising or
falling) on RA3 is detected.

Each of the IOCAF flags is set whenever an enabled edge is detected on its corresponding pin, but note that
these flags can only be cleared by software.

1
larger enhanced mid-range devices, with PORTB pins with an interrupt-on-change facility, have corresponding
IOCBP, IOCBN and IOCBF registers which operate the same way

Enhanced Mid-Range PIC C, Lesson 6: IOC, Sleep Mode and the Watchdog Timer Page 2
© Gooligum Electronics 2014 www.gooligum.com.au

When any of the IOCAF flags are set2, the IOCIF flag in the INTCON register will also be set:
Bit 7 Bit 6 Bit 5 Bit 4 Bit 3 Bit 2 Bit 1 Bit 0

INTCON GIE PEIE TMR0IE INTE IOCIE TMR0IF INTF IOCIF

Thus, the IOCIF flag indicates that an enabled pin change has been detected.

The IOCIE bit, if set, enables interrupt-on-change (IOC).


An IOC interrupt will then be triggered if a valid change occurs on a pin with edge detection enabled, setting
the corresponding flag in the IOCAF register, which in turn sets the IOCIF flag.
Although as always, for any interrupt to occur, the global interrupt enable bit, GIE, must also be set.

Note that the edge detection functionality, which sets flags in the IOCAF register (and hence the IOCIF flag)
when valid pin changes are detected, continues to operate on any pin with edge detection enabled, regardless
of whether the IOCIE bit has been set.

Example 1: Interrupt-on-change (single input)


We’ll start by demonstrating how to use
interrupt-on-change to respond to a single
input, using the circuit from the first external
interrupt example in lesson 5 (shown on the
right), where a pushbutton is connected to
RA2 via a simple RC filter.
If you have the Gooligum baseline and mid-
range training board, close jumpers JP7 and
JP12 to enable the 10 kΩ pull-up resistor on
RA2 and the LED on RA1.
You must also add a 1 µF capacitor (supplied
with the board) between RA2 and ground.
You can do this via pins 13 (‘GP/RA/RB2’)
and 16 (‘GND’) on the 16-pin expansion
header. There should be no need to use the
solderless breadboard – simply plug the
capacitor directly into these header pins.

As we did in that example, we’ll toggle the LED on RA1 whenever the pushbutton is pressed.
RA2 is used for the pushbutton because, on the 12F1501, it has a Schmitt-trigger input, allowing the simple
RC filter to provide effective hardware debouncing, as explained in lesson 3.
This is necessary because, although the switch debouncing could be implemented in software, it is difficult
to do so while responding quickly to changes.

2
or, for devices with PORTB pins with an interrupt-on-change facility, any of the IOCBF flags are set

Enhanced Mid-Range PIC C, Lesson 6: IOC, Sleep Mode and the Watchdog Timer Page 3
© Gooligum Electronics 2014 www.gooligum.com.au

Each pushbutton press will generate a high → low transition (falling edge) on RA2.
So, after configuring and initialising PORTA as usual, we need to enable falling edge detection on RA2:
// configure interrupt-on-change
IOCANbits.IOCAN2 = 1; // enable detection of falling edges on RA2

Note that there is no need to detect rising edges on RA2 – we want to respond immediately each time the
pushbutton is pressed, but we don’t care when it is subsequently released.

We can then enable IOC interrupts:


// enable interrupts
INTCONbits.IOCIE = 1; // enable IOC interrupt
ei(); // enable global interrupts

In the interrupt handler, after having identified the interrupt source, we would usually begin by clearing the
relevant interrupt flag, which in this case is IOCIF.
However, it’s not quite a simple as that with interrupt-on-change, because the IOCIF flag is read-only – you
can’t simply clear it! Instead, this flag represents the overall status of the flags in the IOCAF register3. If
any of the individual IOC flags are set, the IOCIF flag will also be set4.
Therefore we need to clear IOCIF indirectly, by clearing the IOCAF register:
IOCAF = 0; // clear all IOC flags (clears interrupt flag)

This will clear every IOC flag, and hence IOCIF.

Otherwise the code is the same as that in the external interrupt example from lesson 5.

Complete program
Here is how these pieces fit into that previous external interrupt example:
/************************************************************************
* *
* Description: Lesson 6, example 1 *
* *
* Demonstrates use of interrupt-on-change interrupts *
* (without software debouncing) *
* *
* Toggles LED when pushbutton is pressed (high -> low transition) *
* *
*************************************************************************
* *
* Pin assignments: *
* RA1 = indicator LED *
* RA2 = pushbutton (externally debounced, active low) *
* *
************************************************************************/

#include <xc.h>

3
and, for devices with PORTB pins with an interrupt-on-change facility, the flags in the IOCBF register
4
in other words, IOCIF is the result of “logical ORing” every bit in the IOCAF (and, if present, IOCBF) register

Enhanced Mid-Range PIC C, Lesson 6: IOC, Sleep Mode and the Watchdog Timer Page 4
© Gooligum Electronics 2014 www.gooligum.com.au

/***** CONFIGURATION *****/


// ext reset, internal oscillator (no clock out), no watchdog timer
#pragma config MCLRE = ON, FOSC = INTOSC, CLKOUTEN = OFF, WDTE = OFF
// brownout resets enabled, low brownout voltage, no low-power brownout reset
#pragma config BOREN = ON, BORV = LO, LPBOR = OFF
// no power-up timer, no code protect, no write protection
#pragma config PWRTE = OFF, CP = OFF, WRT = OFF
// stack resets on, high-voltage programming
#pragma config STVREN = ON, LVP = OFF

// Pin assignments
#define B_LED LATAbits.LATA1 // "button pressed" indicator LED

/***** MAIN PROGRAM *****/


void main()
{
//*** Initialisation

// configure port
LATA = 0; // start with all output pins low (LED off)
TRISA = 0b111101; // configure RA1 (only) as an output
ANSELA = 0; // disable analog input mode for all pins
// -> RA2 is a digital input

// configure interrupt-on-change
IOCANbits.IOCAN2 = 1; // enable detection of falling edges on RA2

// enable interrupts
INTCONbits.IOCIE = 1; // enable IOC interrupt
ei(); // enable global interrupts

//*** Main loop


for (;;)
{
; // (do nothing)
}
}

/***** INTERRUPT SERVICE ROUTINE *****/


void interrupt isr(void)
{
//*** Service port change interrupt
//
// Triggered on any valid transition on IOC-enabled input pin
// caused by externally debounced pushbutton press
//
// Toggles LED on every enabled transition (e.g. high -> low)
//
// (only port change interrupts are enabled)
//
IOCAF = 0; // clear all IOC flags (clears interrupt flag)

// toggle indicator LED


B_LED = ~B_LED;
}

Enhanced Mid-Range PIC C, Lesson 6: IOC, Sleep Mode and the Watchdog Timer Page 5
© Gooligum Electronics 2014 www.gooligum.com.au

Example 2: Interrupt-on-change (multiple inputs)


This example demonstrates how to handle the situation where interrupt-on-change is enabled on more than
one input pin, using the circuit shown below:

You can build this circuit with the Gooligum baseline and mid-range training board, using the supplied
74HC14 Schmitt-trigger inverter, 1 kΩ and 10 kΩ resistors, 1 µF capacitors and pushbutton switch and
connecting them to signals on the 16-pin header: RA4 input on pin 3 (‘GP/RA/RB4’), RA2 input on pin 13
(‘GP/RA/RB2’) and ground and +5 V on pins15 (‘+V’) and 16 (‘GND’) – using the solderless breadboard, as
illustrated below:

You should also close JP7, JP11 and JP12 to enable the pull-up resistor on RA2 and the LEDs on RA0 and
RA1.

Enhanced Mid-Range PIC C, Lesson 6: IOC, Sleep Mode and the Watchdog Timer Page 6
© Gooligum Electronics 2014 www.gooligum.com.au

If you are using Microchip’s Low Pin Count Demo Board, you can build the circuit in a similar way, by
making connections to the 14-pin header on that board, although of course you will have to supply your own
components and breadboard.

Each pushbutton toggles an LED: S1 controls the LED on RA1, and S2 controls the LED on RA0.

Once again, both buttons are debounced using hardware, to avoid messy software debounce routines (if we
were going to implement software debouncing, we’d be better off using a timer interrupt to poll the inputs, as
we did in lesson 5). For effective hardware debouncing, the simple RC filters need to be coupled with
Schmitt-trigger inputs, and since the only available Schmitt-trigger GP input on the 12F1501 is RA2, an
external Schmitt-trigger inverter is used to drive RA4.
Thus, the operation of S1 is inverted with respect to S2; RA4 is driven high when S1 is pressed, while RA2
is pulled low when S2 is pressed.

Therefore, we need to detect falling edges on RA2 (as before), and rising edges on RA4:
// configure interrupt-on-change
IOCAN = 1<<nPB1; // enable detection of falling edges on PB1 input
IOCAP = 1<<nPB2; // and rising edges on PB2 input

(where ‘nPB1’ and ‘nPB1’ are constants defined as ‘2’ and ‘4’ respectively)

We can then enable IOC interrupts in exactly the same way as before:
// enable interrupts
INTCONbits.IOCIE = 1; // enable IOC interrupt
ei(); // enable global interrupts

The only difference is that we’ve now enabled edge detection on more than one pin.

Then, when handling the port change interrupt in the ISR, we need to determine which pin has changed.
This is essentially the same problem as determining which source generated an interrupt, as we had to do in
the multiple interrupt source example in lesson 5.
We can take the same approach to solving this problem – but instead of testing interrupt flags, we’ll test the
relevant IOC flags in the IOCAF register:
// toggle LED 1 only on button 1 press
if (IOCAF & 1<<nPB1) // if button 1 changed
{
IOCAF &= ~(1<<nPB1); // clear pin change flag
B1_LED = ~B1_LED; // toggle LED 1
}

// toggle LED 2 only on button 2 press


if (IOCAF & 1<<nPB2) // if button 2 changed
{
IOCAF &= ~(1<<nPB2); // clear pin change flag
B2_LED = ~B2_LED; // toggle LED 2
}

Note that, just as in an interrupt hander, where we need to clear the associated interrupt flag, we need to
begin each input handler by clearing the IOC flag (in the IOCAF register) associated with that input.

Enhanced Mid-Range PIC C, Lesson 6: IOC, Sleep Mode and the Watchdog Timer Page 7
© Gooligum Electronics 2014 www.gooligum.com.au

It would be a mistake to clear the whole IOCAF register, as we did in example 1, because the other
pushbutton may also have been pressed, in which case more than one IOC flag will have been set. This
method ensures that no pushbutton presses will be missed.

Complete program
Here is how these pieces fit into the code used in the first example, as well as the multiple interrupt source
example from lesson 5, to form the complete “interrupt-on-change with multiple inputs” program:
/************************************************************************
* *
* Description: Lesson 6, example 2 *
* *
* Demonstrates handling of multiple interrupt-on-change interrupts *
* (without software debouncing) *
* *
* Toggles LED on RA0 when pushbutton on RA2 is pressed *
* (high -> low transition) *
* and LED on RA1 when pushbutton on RA4 is pressed *
* (low -> high transition) *
* *
*************************************************************************
* *
* Pin assignments: *
* RA0 = indicator LED 1 *
* RA1 = indicator LED 2 *
* RA2 = pushbutton 1 (externally debounced, active low) *
* RA4 = pushbutton 2 (externally debounced, active high) *
* *
************************************************************************/

#include <xc.h>

/***** CONFIGURATION *****/


// ext reset, internal oscillator (no clock out), no watchdog timer
#pragma config MCLRE = ON, FOSC = INTOSC, CLKOUTEN = OFF, WDTE = OFF
// brownout resets enabled, low brownout voltage, no low-power brownout reset
#pragma config BOREN = ON, BORV = LO, LPBOR = OFF
// no power-up timer, no code protect, no write protection
#pragma config PWRTE = OFF, CP = OFF, WRT = OFF
// stack resets on, high-voltage programming
#pragma config STVREN = ON, LVP = OFF

// Pin assignments
#define B1_LED LATAbits.LATA0 // "button 1 pressed" indicator LED
#define B2_LED LATAbits.LATA1 // "button 2 pressed" indicator LED
#define nPB1 2 // pushbutton 1 (ext debounce, active low) on RA2
#define nPB2 4 // pushbutton 2 (ext debounce, active high) on RA4

/***** MAIN PROGRAM *****/


void main()
{
//*** Initialisation

// configure port
LATA = 0; // start with all output pins low
TRISA = 0b111100; // configure RA0 and RA1 as outputs
ANSELA = 0; // disable analog input mode for all pins
// -> RA2 and RA4 are digital inputs

Enhanced Mid-Range PIC C, Lesson 6: IOC, Sleep Mode and the Watchdog Timer Page 8
© Gooligum Electronics 2014 www.gooligum.com.au

// configure interrupt-on-change
IOCAN = 1<<nPB1; // enable detection of falling edges on PB1 input
IOCAP = 1<<nPB2; // and rising edges on PB2 input

// enable interrupts
INTCONbits.IOCIE = 1; // enable IOC interrupt
ei(); // enable global interrupts

//*** Main loop


for (;;)
{
; // (do nothing)
}
}

/***** INTERRUPT SERVICE ROUTINE *****/


void interrupt isr(void)
{
//*** Service port change interrupt
//
// Triggered on any valid transition on IOC-enabled input pins
// caused by externally debounced pushbutton press
//
// Toggles LED1 on every valid transition of PB1 (high -> low)
// and LED2 on every valid transition of PB2 (low -> high)
//
// (only port change interrupts are enabled)
//

// toggle LED 1 only on button 1 press


if (IOCAF & 1<<nPB1) // if button 1 changed
{
IOCAF &= ~(1<<nPB1); // clear pin change flag
B1_LED = ~B1_LED; // toggle LED 1
}

// toggle LED 2 only on button 2 press


if (IOCAF & 1<<nPB2) // if button 2 changed
{
IOCAF &= ~(1<<nPB2); // clear pin change flag
B2_LED = ~B2_LED; // toggle LED 2
}
}

Sleep Mode
As mentioned earlier, enhanced mid-range PICs are able to enter a standby, or sleep mode, to save power.
In this mode, the PIC12F1501 will typically draw less than 15 µA (down to only 20 nA for the 12LF1501
variant, with the power supply reduced to 1.8 V), when all of the power-consuming peripherals (such as the
watchdog timer; see later) have been disabled and the output pins are not supplying any current.

Enhanced Mid-Range PIC C, Lesson 6: IOC, Sleep Mode and the Watchdog Timer Page 9
© Gooligum Electronics 2014 www.gooligum.com.au

We’ll use the circuit shown on the right for


the sleep mode examples (as well as those in
the watchdog timer section below).
If you have the Gooligum baseline and mid-
range training board, you can build it by
closing jumpers JP3, JP11 and JP12 to
enable the pull-up resistor on RA3 and the
LEDs on RA0 and RA1.

To demonstrate to yourself that power


consumption really is reduced when the PIC
enters sleep mode, you would have to use an
external power supply, instead of using your
PICkit 3 to power the circuit. You can then
place a multimeter in-line with the power
supply, to measure the supply current.

XC8 provides a ‘SLEEP()’ macro which places the PIC into sleep mode.
It is defined in the “pic.h” header file (called from the “xc.h” file we’ve included at the start of each
program), as:
#define SLEEP() asm("sleep")

‘asm()’ is an XC8 statement which embeds a single assembly language instruction, in-line, in the C source
code. And ‘sleep’ is the assembly language instruction for entering sleep mode (see assembler lesson 8).
Since ‘SLEEP()’ is provided as a standard macro, it makes sense to use it, instead of the ‘asm()’ statement.

To illustrate sleep mode, consider the following fragment of code. It turns on the LED on RA1, waits for the
button to be pressed, and then enters sleep mode:
LATAbits.LATA1 = 1; // turn on LED

while (PORTAbits.RA3 == 1) // wait for button press (low)


;

SLEEP(); // enter sleep mode

for (;;) // (this loop should never execute)


;

Note that the final ‘for (;;)’ statement (an endless loop) will never be executed, because ‘SLEEP()’ will
halt the processor; any instructions after ‘SLEEP()’ will never be reached.

If you run this code5, the LED will turn on and then, when you press the button, nothing will appear to
happen! The LED stays on. Shouldn’t it turn off? What’s going on?

5
as part of a complete program with the usual processor configuration and port initialisation

Enhanced Mid-Range PIC C, Lesson 6: IOC, Sleep Mode and the Watchdog Timer Page 10
© Gooligum Electronics 2014 www.gooligum.com.au

The current supplied from a 5 V supply, before pressing the button, with the LED on, was measured6 to be
7.96 mA. After pressing the button, the measured current dropped to 7.61 mA, a fall of only 0.35 mA.
This happens because, when the PIC goes into standby mode, it stops executing instructions, saving some
power (0.35 mA × 5 V = 1.8 mW in this case), but the I/O ports remain in the state they were in, before the
‘sleep’ instruction was executed.

Note: For low power consumption in standby mode, the I/O ports must be configured to stop
sourcing or sinking current, before entering SLEEP mode.

In this case, the fix is simple – turn off the LED before entering sleep mode, as follows:
LATAbits.LATA1 = 1; // turn on LED

while (PORTAbits.RA3 == 1) // wait for button press (low)


;

LATAbits.LATA1 = 0; // turn off LED

SLEEP(); // enter sleep mode

for (;;) // (this loop should never execute)


;

The LED will now turn off when the button is pressed.

The current measured7 with the PIC in standby and the LED off was now only 18.5 µA.
That was with the unused pins tied to VDD or VSS (whichever is most convenient on the circuit board), as
floating CMOS inputs can lead to unnecessary current draw.

Note: To minimise power in standby mode, configure all unused pins as inputs, and tie them VDD
or VSS through 10 kΩ resistors. Do not connect them directly to VDD or VSS, as the PIC may be
damaged if these pins are inadvertently configured as outputs.

For clarity, tying the unused inputs to VDD or VSS was not shown in the circuit diagram above.

18.5 µA is good, but it is possible to do much better.


The core of the PIC12F1501 is designed for low-voltage operation and cannot operate directly with 5 V
“TTL” logic. Instead, the device includes a voltage regulator, allowing it to be used with a 5 V power supply
and logic levels. This regulator uses some current itself and is the main reason why, at a given power supply
voltage, the PIC12F1501 draws more current than the low-power PIC12LF1501 variant, which does not
include a voltage regulator and is limited to a supply voltage of 3.6 V. If you need low-power operation and
can use 3.3 V or (for lower power) 1.8 V logic, you should use the 12LF1501 instead of the 12F1501.

6
With the minimal circuit built on solderless breadboard. If you leave the PIC in a development board, such as the
Gooligum training board, other devices on the board may draw current, meaning that you are likely to see higher
currents than this. You should nevertheless see a drop in supply current when the PIC enters sleep mode.
7
Again, with the circuit built separately on a breadboard.

Enhanced Mid-Range PIC C, Lesson 6: IOC, Sleep Mode and the Watchdog Timer Page 11
© Gooligum Electronics 2014 www.gooligum.com.au

Although it’s not possible to turn off the voltage regulator in the 12F1501 entirely, it can be made to enter a
low-power state whenever the device enters sleep mode, by setting the VREGPM (voltage regulator power
mode) bit in the VREGCON register:
Bit 7 Bit 6 Bit 5 Bit 4 Bit 3 Bit 2 Bit 1 Bit 0

VREGCON - - - - - - VREGPM (reserved)

So to configure the voltage regulator for low-power sleep mode, we can add to our initialisation code:
// configure sleep mode
VREGCONbits.VREGPM = 1; // enable low-power sleep mode

With this change, the current measured with the PIC in sleep mode was now only 9.1 µA, more than halving
the previous standby mode current.
So why not leave low-power sleep mode enabled all the time? Why is the voltage regulator power mode
configurable? The answer is that, as for most things, there is a trade-off. Wake-up from sleep (see the next
section) takes longer when low-power sleep mode is configured. If you need to respond quickly to an event
which wakes the device, you should not enable low-power sleep mode.
Or, if you need a low standby current as well as fast wake-up from sleep, use the 12LF1501 instead of the
12F1501, if possible.

A standby current of 9.1 µA is still relatively high for a modern microcontroller – so is there anything else
we can turn off, to reduce it further?

Yes – we’ve been operating with brown-out resets (BOR) enabled. As explained briefly in lesson 1, the
PIC12F1501 includes brown-out detection circuitry which (if enabled) will hold the device in reset if the
power supply voltage drops too low. And of course this circuitry draws current.
Brown-out detection is important when the PIC is running, to avoid unreliable operation due to power supply
problems. It’s less of a concern while the device is in sleep mode though. So, an ideal solution, without
having to disable BOR entirely, would be to leave it enabled while the device is running and disable it in
sleep mode.
In fact, this is such a common requirement that the PIC can be configured to do this automatically, by
specifying ‘BOREN = NSLEEP’, instead of ‘BOREN = ON’, in the processor configuration pragmas:
/***** CONFIGURATION *****/
// int reset, internal oscillator (no clock out), no watchdog timer
#pragma config MCLRE = OFF, FOSC = INTOSC, CLKOUTEN = OFF, WDTE = OFF
// brownout reset on (off in sleep), low brownout voltage, no low-power
brownout reset
#pragma config BOREN = NSLEEP, BORV = LO, LPBOR = OFF
// no power-up timer, no code protect, no write protection
#pragma config PWRTE = OFF, CP = OFF, WRT = OFF
// stack resets on, high-voltage programming
#pragma config STVREN = ON, LVP = OFF

With this configuration change, the current measured in sleep mode was now only 0.2 µA – significantly
lower than the 18.5 µA standby current we started with!

Enhanced Mid-Range PIC C, Lesson 6: IOC, Sleep Mode and the Watchdog Timer Page 12
© Gooligum Electronics 2014 www.gooligum.com.au

Complete program
Although it’s very simple, it’s worth seeing how these fragments can form part of a “sleep mode demo”
program:
/************************************************************************
* *
* Description: Lesson 6, example 3c *
* *
* Demonstrates use of low-power sleep mode *
* (voltage regulator in low-power mode and BOR off in sleep) *
* *
* Turn on LED, wait for button press, turn off LED, then sleep *
* *
*************************************************************************
* *
* Pin assignments: *
* RA1 = indicator LED *
* RA3 = pushbutton (active low) *
* *
************************************************************************/

#include <xc.h>

/***** CONFIGURATION *****/


// int reset, internal oscillator (no clock out), no watchdog timer
#pragma config MCLRE = OFF, FOSC = INTOSC, CLKOUTEN = OFF, WDTE = OFF
// brownout reset on (off in sleep), low brownout voltage, no low-power BOR
#pragma config BOREN = NSLEEP, BORV = LO, LPBOR = OFF
// no power-up timer, no code protect, no write protection
#pragma config PWRTE = OFF, CP = OFF, WRT = OFF
// stack resets on, high-voltage programming
#pragma config STVREN = ON, LVP = OFF

// Pin assignments
#define LED LATAbits.LATA1 // indicator LED
#define BUTTON PORTAbits.RA3 // pushbutton (active low)

/***** MAIN PROGRAM *****/


void main()
{
//***** Initialisation

// configure port
TRISA = 0b111101; // configure RA1 (only) as an output

// configure sleep mode


VREGCONbits.VREGPM = 1; // enable low-power sleep mode

//***** Main code

// turn on LED
LED = 1;

// wait for button press


while (BUTTON == 1) // wait until button low
;

Enhanced Mid-Range PIC C, Lesson 6: IOC, Sleep Mode and the Watchdog Timer Page 13
© Gooligum Electronics 2014 www.gooligum.com.au

// go into standby (low power) mode


LED = 0; // turn off LED
SLEEP(); // enter sleep mode

for (;;) // (this loop should never execute)


;
}

Wake-up from Sleep


Sleep mode would not be useful if there was no way to wake up from it – there has to be a way to turn the
device “on” when needed (perhaps in response to an event, such as a button press), after it has been turned
“off”.
Enhanced mid-range PICs provide a number of ways to wake from sleep mode:
 Any device reset, such as an external reset signal on the MCLR pin (if enabled)
 Watchdog timer timeout (see the section on the watchdog timer, later in this lesson)
 Any enabled interrupt source which can set its interrupt flag while in sleep mode
Some interrupt sources cannot be used wake the device from sleep, because, in sleep mode, the PIC’s clock,
or oscillator, is not running. For example, the Timer0 interrupt cannot be used for wake-up from sleep,
because TMR0 does not increment while the PIC is in sleep mode.
However, external (INT pin) and port change interrupts can be used for wake-up, as well as some other
interrupt sources, such as Timer1 and comparators, that will covered in later lessons.
In this lesson, we’ll look at how to use the port change interrupt to wake a PIC from sleep mode; the method
for using an external interrupt is essentially the same, but is of course limited to only the INT pin.

Example 4: Using interrupt-on-change for wake-up from sleep


“Interrupt-on-change” can be used to wake the device from sleep, even if interrupts are not enabled. If port
change interrupts are enabled (IOCIE = 1), but global interrupts are disabled (GIE = 0), then the device will
wake from sleep when an enabled edge is detected, but no interrupt will occur. Program execution simply
continues with the statement following the ‘SLEEP()’ macro.
If port change interrupts are enabled (IOCIE = 1) and global interrupts are enabled (GIE = 1), if an enabled
edge is detected while the PIC is in sleep mode, the device will wake from sleep, execute the assembly
language instruction8 following the sleep instruction, and then enter the interrupt service routine.
If you want the PIC to execute the ISR immediately after it wakes from sleep, you need to enable interrupts
and place a nop instruction (“no operation” – available in XC8 as a ‘NOP()’ macro) immediately following
the sleep instruction.
If you are using other interrupts (such as Timer0) in your program, and don’t want to execute the ISR when
the PIC wakes from sleep, simply disable interrupts (clear GIE – which can be done in XC8 with the ‘di()’
macro) before entering sleep mode.

But regardless of whether interrupts are enabled or not, if IOCIE = 1, the PIC will wake if edge detection is
enabled on one or more pins and a valid edge is detected while the device is in sleep mode.

8
note that a C statement may translate into several assembly language instructions

Enhanced Mid-Range PIC C, Lesson 6: IOC, Sleep Mode and the Watchdog Timer Page 14
© Gooligum Electronics 2014 www.gooligum.com.au

It is important to clear the IOC flags (clearing IOCIF) before entering sleep mode, or else the PIC will wake
immediately.
It is also important to ensure that any input which will be used to trigger a wake-up is stable before entering
sleep mode. Consider what would happen if interrupt-on-change was enabled for button presses (falling
edges on RA3) in the program above. As soon as the button is pressed, the LED will turn off and the PIC
will enter standby mode, as intended. But on the first switch bounce, a falling edge will be detected on RA3,
and the PIC would wake.
So, to successfully use a single pushbutton as an on/off control, turning the circuit (PIC and LED) “off” as
before, but then having that pushbutton wake the device when it is pressed again, it is necessary to wait for
the button to be released and remain stable (debounced)9 before entering sleep mode.

But there’s still a potential problem. PICs are fast, and human fingers are slow – if, as soon as the PIC wakes
from sleep, the program immediately checks for a “turn off” button press, the button will still be down, as
part of the button press which woke the PIC from sleep, and the LED will immediately turn off again. To
avoid this, we must wait for the button to be in a stable “up” state before checking that it is “down”.
So the necessary sequence (in pseudo-code) is:
loop
turn on LED
wait for stable button high
wait for button low
turn off LED
wait for stable button high
clear IOC flags
sleep
goto loop ; repeat from the beginning

The following code, which uses the debounce macro defined in lesson 4, implements this:
//*** Initialisation

// configure port
TRISA = 0b111101; // configure RA1 (only) as an output

// configure interrupt-on-change
IOCANbits.IOCAN3 = 1; // enable detection of falling edges on RA3
INTCONbits.IOCIE = 1; // enable wake-up (interrupt) on port change

// configure oscillator
OSCCONbits.SCS1 = 1; // select internal clock
OSCCONbits.IRCF = 0b0111; // internal oscillator = 500 kHz

// configure Timer0 (for DbnceHi() macro)


OPTION_REGbits.TMR0CS = 0; // select timer mode
OPTION_REGbits.PSA = 0; // assign prescaler to Timer0
OPTION_REGbits.PS = 0b010; // prescale = 8
// -> increment TMR0 every 64 us

// configure sleep mode


VREGCONbits.VREGPM = 1; // enable low-power sleep mode

9
unless the switch is externally debounced

Enhanced Mid-Range PIC C, Lesson 6: IOC, Sleep Mode and the Watchdog Timer Page 15
© Gooligum Electronics 2014 www.gooligum.com.au

//*** Main loop


for (;;)
{
// turn on LED
LED = 1;

// wait for stable button high


// (in case it is still bouncing after wakeup)
DbnceHi(BUTTON);

// wait for button press


while (BUTTON == 1) // wait until button low
;

// go into standby (low power) mode


LED = 0; // turn off LED
DbnceHi(BUTTON); // wait for stable button release
IOCAF = 0; // clear IOC flags (clears port change flag)
SLEEP(); // enter sleep mode
}

(the labels ‘LED’ and ‘BUTTON’ are defined earlier in the program, as usual)
This code does essentially the same thing as the “toggle an LED” programs developed in lesson 3, except
that in this case, when the LED is off, the PIC is drawing negligible power.

Watchdog Timer
In the real world, computer programs sometimes “crash”; they will stop responding to input, stuck in a
continuous loop they can’t get out of, and the only way out is to reset the processor (e.g. Ctrl-Alt-Del on
Windows PCs – and even that sometimes won’t work, and you need to power cycle a PC to bring it back).
Microcontrollers are not immune to this. Their programs can become stuck because some unforseen
sequence of inputs has occurred, or perhaps because an expected input signal never arrives. Or, in the
electrically noisy industrial environment in which microcontrollers are often operating, power glitches and
EMI on signal lines can create an unstable environment, perhaps leading to a crash.
Crashes present a special problem for equipment which is intended to be reliable, operating autonomously, in
environments where user intervention isn’t an option.
One of the major functions of a watchdog timer is to automatically reset the microcontroller in the event of a
crash. It is simply a free-running timer (running independently of any other processor function) which, if
allowed to overflow (or time-out), will reset the PIC. In normal operation, an instruction which clears the
watchdog timer is regularly executed – often enough to prevent the timer ever overflowing. It is common to
place this instruction in the “main loop” of a program, where it would normally be expected to be executed
often enough to prevent watchdog timer overflows. If the program crashes, the main loop presumably won’t
complete; the watchdog timer won’t be cleared, and the PIC will be reset. Hopefully, when the PIC restarts,
whatever condition led to the crash will have gone away, and the PIC will resume normal operation.

The assembly language instruction for clearing the watchdog timer is ‘clrwdt’, which XC8 makes available
as a macro: ‘CLRWDT()’.

The time-out period is configurable, using the WDTPS bits in the WDTCON register:
Bit 7 Bit 6 Bit 5 Bit 4 Bit 3 Bit 2 Bit 1 Bit 0
WDTCON – – WDTPS4 WDTPS3 WDTPS2 WDTPS1 WDTPS0 SWDTEN

Enhanced Mid-Range PIC C, Lesson 6: IOC, Sleep Mode and the Watchdog Timer Page 16
© Gooligum Electronics 2014 www.gooligum.com.au

The following time-out periods can be selected:

WDTPS<4:0> WDT time-out period The watchdog timer is driven, via a


bit value prescale ratio (nominal) prescaler, by a 31 kHz low-frequency
oscillator – the LFINTOSC oscillator
00000 1 : 32 1 ms mentioned in lesson 2.
00001 1 : 64 2 ms This oscillator is uncalibrated and can
00010 1 : 128 4 ms vary by 10% or more with temperature
and supply voltage, so these time-out
00011 1 : 256 8 ms periods must be considered very much
00100 1 : 512 16 ms approximate.

00101 1 : 1024 32 ms
As you can see, the WDTPS<4:0> bits
00110 1 : 2048 64 ms
select a WDT prescale ratio between 1:32
00111 1 : 4096 128 ms and 1:223 (1:8388608), corresponding to
nominal time-out periods ranging from 1
01000 1 : 8192 256 ms
ms to 256 s, as shown.
01001 1 : 16384 512 ms
WDTPS bit values above 10010 are
01010 1 : 32768 1s “reserved” in the PIC12F1501 (i.e. not
used) and select the minimum prescale
01011 (default) 1 : 65536 2s
ratio of 1:32.
17
01100 1:2 4s
18
01101 1:2 8s
The default WDT prescale ratio,
01110 1 : 219 16 s following a power-on reset, is 1:65536,
corresponding to a nominal watchdog
01111 1 : 220 32 s
time-out period of 2 seconds.
10000 1 : 221 64 s
10001 1 : 222 128 s
10010 1 : 223 256 s

The watchdog timer is controlled by the WDTE bits in the PIC12F1501’s first processor configuration word:
Bit 13 12 11 10 9 8 7 6 5 4 3 2 1 Bit 0

- - CLKOUTEN BOREN<1:0> - CP MCLRE PRWTE WDTE<1:0> - FOSC<1:0>

The following watchdog timer configuration options are available:

WDTE<1:0> configuration pragma Watchdog timer configuration


00 WDTE = OFF always disabled
01 WDTE = SWDTEN controlled by SWDTEN bit
10 WDTE = NSLEEP enabled while running and disabled in sleep
11 WDTE = ON always enabled

Enhanced Mid-Range PIC C, Lesson 6: IOC, Sleep Mode and the Watchdog Timer Page 17
© Gooligum Electronics 2014 www.gooligum.com.au

‘WDTE = ON’ means that the watchdog timer is always on.


We’ll see in the next section that the watchdog timer can be used to periodically wake the device from sleep
mode. But if you’re not using that functionality, it’s better to disable the watchdog timer in sleep mode to
minimise standby current, as we did earlier brown-out resets. This can be done automatically, by selecting
the ‘WDTE = NSLEEP’ configuration option.
Finally, the watchdog timer can be configured for software control, via the ‘WDTE = SWDTEN’ option. It is
then controlled by the SWDTEN bit, in the WDTCON register: the watchdog timer is enabled if SWDTEN
is set and disabled if it is clear. You can then enable the watchdog timer only when it is needed, such as if
you’re using the WDT to wake the device periodically from sleep, but don’t want to have it enabled during
code execution, to avoid the need to periodically run ‘clrwdt’ instructions (via the ‘CLRWDT()’ macro).

Example 5a: Watchdog Timer


To show how the watchdog timer allows the PIC to recover from a crash, we’ll use a simple program which
turns on an LED for 1 sec, turns it off again, and then enters an endless loop (simulating a crash).
If the watchdog timer is disabled, the loop will never exit and the LED will remain off.
But if the watchdog timer is enabled, with a period of 2 sec, the program should restart itself after 2 sec, and
the LED will flash: on for 1 sec and off for 1 sec (approximately).

To make it easy to test configurations with the watchdog timer on or off, you can use a construct, as part of
the processor configuration pragmas, such as:
#define WATCHDOG // define to enable watchdog timer

#ifdef WATCHDOG
// no watchdog timer
#pragma config WDTE = ON
#else
// no watchdog timer
#pragma config WDTE = OFF
#endif

The watchdog timer prescaler is set to 1:65536, for a nominal time-out period of 2 sec, by:
// configure watchdog timer
WDTCONbits.WDTPS = 0b01011; // prescale = 65536 (-> WDT timeout = 2 sec)

Note that this is the default prescale value, but, as we’ve been doing with the internal RC oscillator
configuration, it doesn’t hurt to explicitly configure it like this, to make it clear that we’ve chosen to use this
prescaler configuration.

The code to flash the LED once and then enter an endless loop is straightforward:
LED = 1; // turn on LED

__delay_ms(1000); // delay 1 sec

LED = 0; // turn off LED

for (;;) // wait forever


;

Enhanced Mid-Range PIC C, Lesson 6: IOC, Sleep Mode and the Watchdog Timer Page 18
© Gooligum Electronics 2014 www.gooligum.com.au

Complete program
If you build and run this program, with ‘#define WATCHDOG’ commented out (place a ‘//’ in front of it, or
change the ‘#define’ to ‘#undef’), the LED will light once, and then remain off. But if you leave the
symbol ‘WATCHDOG’ defined, the LED will continue to flash:
/************************************************************************
* *
* Description: Lesson 6, example 5a *
* *
* Demonstrates use of watchdog timer *
* *
* Turn on LED for 1 s, turn off, then enter endless loop *
* LED stays off if watchdog not enabled, flashes if WDT set to 2 sec *
* *
*************************************************************************
* *
* Pin assignments: *
* RA1 = indicator LED *
* *
************************************************************************/

#include <xc.h>

#define _XTAL_FREQ 500000 // oscillator frequency for _delay()

/***** CONFIGURATION *****/


#define WATCHDOG // define to enable watchdog timer

#ifdef WATCHDOG
// no watchdog timer
#pragma config WDTE = ON
#else
// no watchdog timer
#pragma config WDTE = OFF
#endif

// ext reset, internal oscillator (no clock out)


#pragma config MCLRE = ON, FOSC = INTOSC, CLKOUTEN = OFF
// brownout resets enabled, low brownout voltage, no low-power brownout reset
#pragma config BOREN = ON, BORV = LO, LPBOR = OFF
// no power-up timer, no code protect, no write protection
#pragma config PWRTE = OFF, CP = OFF, WRT = OFF
// stack resets on, high-voltage programming
#pragma config STVREN = ON, LVP = OFF

// Pin assignments
#define LED LATAbits.LATA1 // indicator LED

/***** MAIN PROGRAM *****/


void main()
{
//*** Initialisation

// configure port
TRISA = 0b111101; // configure RA1 (only) as an output

// configure oscillator
OSCCONbits.SCS1 = 1; // select internal clock
OSCCONbits.IRCF = 0b0111; // internal oscillator = 500 kHz

Enhanced Mid-Range PIC C, Lesson 6: IOC, Sleep Mode and the Watchdog Timer Page 19
© Gooligum Electronics 2014 www.gooligum.com.au

// configure watchdog timer


WDTCONbits.WDTPS = 0b01011; // prescale = 65536 (-> WDT timeout = 2 sec)

//*** Main code


LED = 1; // turn on LED

__delay_ms(1000); // delay 1 sec

LED = 0; // turn off LED

for (;;) // wait forever


;
}

Example 5b: Detecting a WDT time-out reset


When the watchdog timer overflows, the PIC is reset, your program is restarted, in the same way that is was
when power was first applied, or after an MCLR reset.
But you may want your program to behave differently, depending on why it was restarted. In particular, if a
WDT time-out reset has occurred, you may wish to reset some external equipment to a known state, or
perhaps simply turn on an alarm indicator to show that something has gone wrong.
Watchdog timer resets are indicated by the TO flag in the STATUS register10:
Bit 7 Bit 6 Bit 5 Bit 4 Bit 3 Bit 2 Bit 1 Bit 0

STATUS - - - TO PD Z DC C

The TO (time-out) flag is cleared to ‘0’ by a WDT time-out reset.


It is set to ‘1’ at power-on, or by entering sleep mode, or execution of the ‘clrwdt’ instruction.

Thus, if TO has been cleared, it means that a WDT time-out reset has occurred.

To demonstrate how the TO flag is used, the previous example can be modified to light a second LED when
a watchdog timer reset has occurred, but not when the PIC is first powered on, as follows:
//*** Main code

// test for WDT-timeout reset


if (!STATUSbits.nTO) // if WDT timeout has occurred,
WDT = 1; // turn on "error" LED

// flash LED
LED = 1; // turn on "flash" LED
__delay_ms(1000); // delay 1 sec
LED = 0; // turn off "flash" LED

// wait forever
for (;;)
;

10
it is also indicated, in a similar way, by the RWDT flag in the PCON register – see the data sheet for details

Enhanced Mid-Range PIC C, Lesson 6: IOC, Sleep Mode and the Watchdog Timer Page 20
© Gooligum Electronics 2014 www.gooligum.com.au

When testing the TO flag, the test condition is inverted, using ‘!’, since this flag is “active” when clear.

Note that, if, after the watchdog timer has reset the PIC, and the “WDT” LED has been lit, you then use the
reset button to restart the program, the “WDT” LED will remain lit. This is because a MCLR reset does not
affect the TO flag – you need to power cycle the PIC to reset the TO flag.

Example 5c: Clearing the watchdog timer


Normally, you will want to prevent watchdog timer overflows; a WDT reset should only happen when
something has gone wrong.
As discussed earlier, to avoid WDT resets, the watchdog timer has to be regularly cleared.
This is typically done by inserting a ‘CLRWDT()’ macro within the program’s “main loop”, and within any
function which may, in normal operation, not complete within the watchdog timer period.
A watchdog timer period should be selected which is long enough to ensure that the watchdog timer never
expires within the loop, unless something is wrong. For example, if your main loop normally completes
within 10 ms, but can sometimes take up to 40 ms, you would select a watchdog period of 64 ms (prescale
ratio = 1:2048) or perhaps 128 ms (prescale = 1:4096) to be sure.

To demonstrate that the ‘CLRWDT()’ macro really does stop the watchdog expiring (if executed often
enough), simply include it in the endless loop at the end of the code:
//*** Main code
LED = 1; // turn on LED

__delay_ms(1000); // delay 1 sec

LED = 0; // turn off LED

for (;;) // repeatedly clear watchdog timer forever


CLRWDT();

Periodic Wake from Sleep


The watchdog timer can also be used to wake the PIC from sleep mode.
This is useful in situations where inputs do not need to be responded to instantly, but can be checked
periodically. To minimise power consumption, the PIC can sleep most of the time, waking up every so often
(say, once every few seconds), checking inputs and, if there is nothing to do, going back to sleep.
Note that a periodic wake-up can be combined with wake-up on pin change; you may for example wish to
periodically log the value of a sensor, but also respond immediately to button presses.

If the watchdog timer expires while the PIC is in sleep mode, the device wakes from sleep, and program
execution continues with the statement following SLEEP(). No device reset occurs.

The sleep instruction (and hence the SLEEP() macro) clears the watchdog timer and prescaler. This means
that the device will sleep for however long the watchdog timer period is set to (unless another event wakes it
before the WDT expires).

Enhanced Mid-Range PIC C, Lesson 6: IOC, Sleep Mode and the Watchdog Timer Page 21
© Gooligum Electronics 2014 www.gooligum.com.au

To demonstrate how this works, we can simply convert the main code in example 5a into a loop,
incorporating the ‘SLEEP()’ macro:
//*** Main loop
for (;;)
{
LED = 1; // turn on LED

__delay_ms(1000); // delay 1 sec

LED = 0; // turn off LED

SLEEP(); // enter sleep mode (until WDT time-out)


}

If you enable the watchdog timer, you’ll find that the LED turns on for 1 s, and is then off for around 2 s,
before turning on again. And if you measure the current drawn by the PIC, you will find that very little
power is consumed while the LED is off, because the PIC is in sleep mode.
To make it easier to observe the standby current, we can increase the time-out period to, say, 8 seconds:
// configure watchdog timer
WDTCONbits.WDTPS = 0b01101; // prescale = 2^18 (-> WDT period = 8 sec)

Regardless of the time-out period, if you disable the watchdog timer, the LED will turn on for 1 s, but then
turn off forever, because, with the watchdog disabled, the PIC never wakes from sleep.

Complete program
Here is how this new main loop fits into the program presented in example 5a:
/************************************************************************
* *
* Description: Lesson 6, example 6 *
* *
* Demonstrates periodic wake from sleep, using the watchdog timer *
* *
* Turn on LED for 1s, turn off, then sleep *
* LED stays off if watchdog not enabled, *
* flashes (1s on, 8 off) if WDT enabled *
* *
*************************************************************************
* *
* Pin assignments: *
* RA1 = indicator LED *
* *
************************************************************************/

#include <xc.h>

#define _XTAL_FREQ 500000 // oscillator frequency for _delay()

/***** CONFIGURATION *****/


#define WATCHDOG // define to enable watchdog timer

#ifdef WATCHDOG
// no watchdog timer
#pragma config WDTE = ON

Enhanced Mid-Range PIC C, Lesson 6: IOC, Sleep Mode and the Watchdog Timer Page 22
© Gooligum Electronics 2014 www.gooligum.com.au

#else
// no watchdog timer
#pragma config WDTE = OFF
#endif

// ext reset, internal oscillator (no clock out)


#pragma config MCLRE = ON, FOSC = INTOSC, CLKOUTEN = OFF
// brownout resets enabled, low brownout voltage, no low-power brownout reset
#pragma config BOREN = ON, BORV = LO, LPBOR = OFF
// no power-up timer, no code protect, no write protection
#pragma config PWRTE = OFF, CP = OFF, WRT = OFF
// stack resets on, high-voltage programming
#pragma config STVREN = ON, LVP = OFF

// Pin assignments
#define LED LATAbits.LATA1 // indicator LED

/***** MAIN PROGRAM *****/


void main()
{
//*** Initialisation

// configure port
TRISA = 0b111101; // configure RA1 (only) as an output

// configure oscillator
OSCCONbits.SCS1 = 1; // select internal clock
OSCCONbits.IRCF = 0b0111; // internal oscillator = 500 kHz

// configure watchdog timer


WDTCONbits.WDTPS = 0b01101; // prescale = 2^18 (-> WDT period = 8 sec)

//*** Main loop


for (;;)
{
LED = 1; // turn on LED

__delay_ms(1000); // delay 1 sec

LED = 0; // turn off LED

SLEEP(); // enter sleep mode (until WDT time-out)


}
}

Enhanced Mid-Range PIC C, Lesson 6: IOC, Sleep Mode and the Watchdog Timer Page 23
© Gooligum Electronics 2014 www.gooligum.com.au

Conclusion
We’ve seen in this lesson that a specified change on a digital input pin can be used to interrupt enhanced
mid-range PICs and that we can then easily determine which pin changed.
We also saw that enhanced mid-range PICs can be put into a low-power sleep mode, and that they can be
made to wake up by an external event (such as a pin change), or on a regular basis by the watchdog timer,
which is also (in fact, primarily) useful for restarting the device if it gets “stuck”, following some type of
error condition.
So far in this tutorial series we’ve focussed on programming and the internal architecture of enhanced mid-
range PICs, but in the next lesson we’ll dive into hardware – taking a look at features related to the power
supply, such as brown-out detection and the power-up timer, and the available oscillator (clock) options,
after introducing the 14-pin PIC16F1824.

Enhanced Mid-Range PIC C, Lesson 6: IOC, Sleep Mode and the Watchdog Timer Page 24

You might also like